Adding support for mounting NTFS drives in the Controller. (#440)

This commit is contained in:
lrnselfreliance 2026-05-15 19:25:43 -06:00 • committed by GitHub
parent 09e9af531a
commit 8f0e4cd93f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 164 additions and 13 deletions

86
app/package-lock.json generated
View file

@ -107,6 +107,7 @@
"version": "7.20.12",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz",
"integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6",
@ -913,6 +914,7 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz",
"integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6"
},
@ -1474,6 +1476,7 @@
"version": "7.20.13",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz",
"integrity": "sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw==",
"peer": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-module-imports": "^7.18.6",
@ -3398,6 +3401,7 @@
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@ -4187,6 +4191,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@ -4197,6 +4202,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^18.0.0"
}
@ -4320,6 +4326,7 @@
"version": "5.50.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz",
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.50.0",
"@typescript-eslint/type-utils": "5.50.0",
@ -4371,6 +4378,7 @@
"version": "5.50.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz",
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.50.0",
"@typescript-eslint/types": "5.50.0",
@ -4701,6 +4709,7 @@
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -4823,6 +4832,7 @@
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -5680,6 +5690,7 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@ -6477,6 +6488,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -6763,6 +6775,7 @@
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.2.tgz",
"integrity": "sha512-ARbnUorjcCM3XiPwgHKuqsyr5W9Qn+pIIBPaoilnoBkLdSC2oLQjV1BUpnmc7KR+b7Avah3Ly2RMFnfxr96E/A==",
"hasInstallScript": true,
"peer": true,
"dependencies": {
"@cypress/request": "^3.0.6",
"@cypress/xvfb": "^1.2.4",
@ -7529,6 +7542,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"peer": true,
"dependencies": {
"ansi-colors": "^4.1.1",
"strip-ansi": "^6.0.1"
@ -7808,6 +7822,7 @@
"version": "8.33.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
"peer": true,
"dependencies": {
"@eslint/eslintrc": "^1.4.1",
"@humanwhocodes/config-array": "^0.11.8",
@ -8221,6 +8236,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -10712,6 +10728,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
"peer": true,
"dependencies": {
"@jest/core": "^27.5.1",
"import-local": "^3.0.2",
@ -13408,6 +13425,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -14249,6 +14267,7 @@
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"peer": true,
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@ -15307,6 +15326,7 @@
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
"integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@ -15496,6 +15516,7 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"peer": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@ -15704,6 +15725,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@ -15857,6 +15879,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
@ -15968,6 +15991,7 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -16482,6 +16506,7 @@
"version": "2.80.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@ -16763,6 +16788,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-2.1.4.tgz",
"integrity": "sha512-7CxjBoFUfH7fUvtn+SPkkIocthUD9kV3niF1mUMa9TbeyPAf2brtRCZBlT2OpHaXmkscFzGjEfhbJo9gKfotzQ==",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.10.5",
"@fluentui/react-component-event-listener": "~0.63.0",
@ -17850,6 +17876,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -17921,7 +17948,8 @@
"node_modules/three": {
"version": "0.154.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.154.0.tgz",
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==",
"peer": true
},
"node_modules/three-stdlib": {
"version": "2.17.2",
@ -18170,6 +18198,7 @@
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"peer": true,
"engines": {
"node": ">=10"
},
@ -18223,6 +18252,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -18534,6 +18564,7 @@
"version": "5.105.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
"integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@ -18603,6 +18634,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -18652,6 +18684,7 @@
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz",
"integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==",
"peer": true,
"dependencies": {
"@types/bonjour": "^3.5.9",
"@types/connect-history-api-fallback": "^1.3.5",
@ -18706,6 +18739,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -18818,6 +18852,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -19100,6 +19135,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -19544,6 +19580,7 @@
"version": "7.20.12",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz",
"integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==",
"peer": true,
"requires": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.18.6",
@ -20093,6 +20130,7 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz",
"integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==",
"peer": true,
"requires": {
"@babel/helper-plugin-utils": "^7.18.6"
}
@ -20438,6 +20476,7 @@
"version": "7.20.13",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz",
"integrity": "sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw==",
"peer": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-module-imports": "^7.18.6",
@ -21789,7 +21828,8 @@
"@popperjs/core": {
"version": "2.11.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
"integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"peer": true
},
"@react-three/fiber": {
"version": "8.16.5",
@ -22367,6 +22407,7 @@
"version": "18.3.27",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"peer": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@ -22376,6 +22417,7 @@
"version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"peer": true,
"requires": {}
},
"@types/react-reconciler": {
@ -22496,6 +22538,7 @@
"version": "5.50.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz",
"integrity": "sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ==",
"peer": true,
"requires": {
"@typescript-eslint/scope-manager": "5.50.0",
"@typescript-eslint/type-utils": "5.50.0",
@ -22521,6 +22564,7 @@
"version": "5.50.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.50.0.tgz",
"integrity": "sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==",
"peer": true,
"requires": {
"@typescript-eslint/scope-manager": "5.50.0",
"@typescript-eslint/types": "5.50.0",
@ -22770,7 +22814,8 @@
"acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"peer": true
},
"acorn-globals": {
"version": "6.0.0",
@ -22857,6 +22902,7 @@
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -23474,6 +23520,7 @@
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"peer": true,
"requires": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@ -24015,6 +24062,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -24223,6 +24271,7 @@
"version": "13.15.2",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.2.tgz",
"integrity": "sha512-ARbnUorjcCM3XiPwgHKuqsyr5W9Qn+pIIBPaoilnoBkLdSC2oLQjV1BUpnmc7KR+b7Avah3Ly2RMFnfxr96E/A==",
"peer": true,
"requires": {
"@cypress/request": "^3.0.6",
"@cypress/xvfb": "^1.2.4",
@ -24786,6 +24835,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
"peer": true,
"requires": {
"ansi-colors": "^4.1.1",
"strip-ansi": "^6.0.1"
@ -25004,6 +25054,7 @@
"version": "8.33.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz",
"integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==",
"peer": true,
"requires": {
"@eslint/eslintrc": "^1.4.1",
"@humanwhocodes/config-array": "^0.11.8",
@ -25379,6 +25430,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -27049,6 +27101,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
"peer": true,
"requires": {
"@jest/core": "^27.5.1",
"import-local": "^3.0.2",
@ -29020,6 +29073,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -29612,6 +29666,7 @@
"version": "8.4.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
"peer": true,
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@ -30195,6 +30250,7 @@
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz",
"integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==",
"peer": true,
"requires": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@ -30334,6 +30390,7 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"peer": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
@ -30491,6 +30548,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0"
}
@ -30604,6 +30662,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
@ -30680,7 +30739,8 @@
"react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
"peer": true
},
"react-router": {
"version": "7.12.0",
@ -31049,6 +31109,7 @@
"version": "2.80.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
"peer": true,
"requires": {
"fsevents": "~2.3.2"
}
@ -31239,6 +31300,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-2.1.4.tgz",
"integrity": "sha512-7CxjBoFUfH7fUvtn+SPkkIocthUD9kV3niF1mUMa9TbeyPAf2brtRCZBlT2OpHaXmkscFzGjEfhbJo9gKfotzQ==",
"peer": true,
"requires": {
"@babel/runtime": "^7.10.5",
"@fluentui/react-component-event-listener": "~0.63.0",
@ -32068,6 +32130,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -32119,7 +32182,8 @@
"three": {
"version": "0.154.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.154.0.tgz",
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug=="
"integrity": "sha512-Uzz8C/5GesJzv8i+Y2prEMYUwodwZySPcNhuJUdsVMH2Yn4Nm8qlbQe6qRN5fOhg55XB0WiLfTPBxVHxpE60ug==",
"peer": true
},
"three-stdlib": {
"version": "2.17.2",
@ -32321,7 +32385,8 @@
"type-fest": {
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
"peer": true
},
"type-is": {
"version": "1.6.18",
@ -32358,7 +32423,8 @@
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true
},
"unbox-primitive": {
"version": "1.0.2",
@ -32585,6 +32651,7 @@
"version": "5.105.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
"integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
"peer": true,
"requires": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@ -32617,6 +32684,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -32680,6 +32748,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -32717,6 +32786,7 @@
"version": "4.11.1",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz",
"integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==",
"peer": true,
"requires": {
"@types/bonjour": "^3.5.9",
"@types/connect-history-api-fallback": "^1.3.5",
@ -32753,6 +32823,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -32999,6 +33070,7 @@
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"peer": true,
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",

View file

@ -18,7 +18,7 @@ from controller.lib.config import is_docker_mode
logger = logging.getLogger(__name__)
SUPPORTED_FILESYSTEMS = {"ext4", "btrfs", "vfat", "exfat"}
SUPPORTED_FILESYSTEMS = {"ext4", "btrfs", "vfat", "exfat", "ntfs", "ntfs3"}
def get_wrolpi_uid_gid() -> tuple[int, int]:
@ -171,9 +171,10 @@ def mount_drive(
# Create mount point if needed
Path(mount_point).mkdir(parents=True, exist_ok=True)
# For exfat/vfat, add uid/gid options to set ownership to wrolpi user
# These filesystems don't support POSIX permissions natively
if fstype in ("exfat", "vfat"):
# For filesystems without native POSIX permissions, set ownership to
# the wrolpi user via uid/gid mount options. Covers FAT family and
# both NTFS drivers (ntfs-3g FUSE and the in-kernel ntfs3).
if fstype in ("exfat", "vfat", "ntfs", "ntfs3"):
uid, gid = get_wrolpi_uid_gid()
options = f"{options},uid={uid},gid={gid}"
@ -281,6 +282,13 @@ def check_shadowed_data(target: str) -> Optional[dict]:
for entry in entries:
entry_path = target_path / entry
try:
# Skip symlinks: a symlink-to-directory at the mount target would
# otherwise be walked, inflating size_bytes with content from
# outside the would-be-shadowed area (e.g. /etc, /). The symlink
# itself is still listed in `entries` so the user is warned about
# it; it just contributes 0 bytes.
if entry_path.is_symlink():
continue
if entry_path.is_dir():
for path in entry_path.rglob("*"):
try:
@ -289,7 +297,7 @@ def check_shadowed_data(target: str) -> Optional[dict]:
except OSError:
# File disappeared mid-walk or unreadable; skip.
continue
elif entry_path.is_file() and not entry_path.is_symlink():
elif entry_path.is_file():
size_bytes += entry_path.stat().st_size
except OSError:
continue

View file

@ -107,7 +107,7 @@ class TestGetBlockDevices:
"type": "disk",
"children": [
{"name": "sda1", "path": "/dev/sda1", "size": "100G", "type": "part", "fstype": "ext4"},
{"name": "sda2", "path": "/dev/sda2", "size": "100G", "type": "part", "fstype": "ntfs"},
{"name": "sda2", "path": "/dev/sda2", "size": "100G", "type": "part", "fstype": "xfs"},
]
}
]
@ -318,6 +318,36 @@ class TestMountDrive:
assert "uid=1001" in options
assert "gid=1001" in options
def test_ntfs_mount_includes_uid_gid(self):
"""Should add uid/gid options when mounting ntfs (Windows-formatted drive)."""
with mock.patch("controller.lib.disks.is_docker_mode", return_value=False):
with mock.patch("controller.lib.disks.get_wrolpi_uid_gid", return_value=(1001, 1001)):
with mock.patch("pathlib.Path.mkdir"):
with mock.patch("subprocess.run") as mock_run:
mock_run.return_value = mock.Mock(returncode=0, stdout="", stderr="")
result = mount_drive("/dev/sda1", "/media/test", fstype="ntfs")
assert result["success"] is True
call_args = mock_run.call_args[0][0]
options_idx = call_args.index("-o") + 1
options = call_args[options_idx]
assert "uid=1001" in options
assert "gid=1001" in options
def test_ntfs3_mount_includes_uid_gid(self):
"""Same handling for the in-kernel ntfs3 driver (kernel 5.15+)."""
with mock.patch("controller.lib.disks.is_docker_mode", return_value=False):
with mock.patch("controller.lib.disks.get_wrolpi_uid_gid", return_value=(1001, 1001)):
with mock.patch("pathlib.Path.mkdir"):
with mock.patch("subprocess.run") as mock_run:
mock_run.return_value = mock.Mock(returncode=0, stdout="", stderr="")
result = mount_drive("/dev/sda1", "/media/test", fstype="ntfs3")
assert result["success"] is True
call_args = mock_run.call_args[0][0]
options_idx = call_args.index("-o") + 1
options = call_args[options_idx]
assert "uid=1001" in options
assert "gid=1001" in options
class TestGetWrolpiUidGid:
"""Tests for get_wrolpi_uid_gid function."""
@ -457,4 +487,43 @@ class TestCheckShadowedData:
assert result["size_bytes"] == 1000
assert result["entries"] == ["videos", "zims"]
def test_does_not_follow_symlinks_to_directories(self, tmp_path):
"""A symlink at the mount target must not have its target walked.
Regression: previously a symlink-to-dir was detected by is_dir()
(which follows symlinks), then rglob walked the target — so
`ln -s /etc /media/wrolpi/x` would sum the size of /etc.
"""
outside = tmp_path / "outside"
outside.mkdir()
# A few large files outside the mount target.
(outside / "a").write_bytes(b"x" * 10_000)
(outside / "b").write_bytes(b"x" * 20_000)
target = tmp_path / "wrolpi"
target.mkdir()
(target / "videos").mkdir()
(target / "videos" / "movie.mp4").write_bytes(b"x" * 100)
# Symlink pointing outside the mount target.
(target / "external").symlink_to(outside)
result = check_shadowed_data(str(target))
# `external` is listed (the user should know it's there) but it
# contributes 0 to the size — only the real `videos` data counts.
assert result["entries"] == ["external", "videos"]
assert result["size_bytes"] == 100
def test_does_not_follow_symlinks_to_files(self, tmp_path):
"""A symlink-to-file is listed but contributes 0 bytes."""
outside_file = tmp_path / "big.bin"
outside_file.write_bytes(b"x" * 50_000)
target = tmp_path / "wrolpi"
target.mkdir()
(target / "link.bin").symlink_to(outside_file)
result = check_shadowed_data(str(target))
assert result["entries"] == ["link.bin"]
assert result["size_bytes"] == 0

View file

@ -52,6 +52,7 @@ network-manager
nicstat
nodejs
npm
ntfs-3g
pkg-config
postgresql-15
postgresql-doc

View file

@ -52,6 +52,7 @@ network-manager
nicstat
nodejs
npm
ntfs-3g
pkg-config
postgresql-15
postgresql-doc