Compare commits

..

No commits in common. "master" and "v4.04" have entirely different histories.

20 changed files with 534 additions and 627 deletions

View file

@ -1,6 +0,0 @@
{
"name": "windows",
"service": "windows",
"forwardPorts": [8006],
"dockerComposeFile": "compose.yml"
}

View file

@ -21,7 +21,6 @@ body:
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
@ -29,7 +28,6 @@ body:
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs windows`).
render: shell
validations:
required: true
- type: textarea

View file

@ -23,7 +23,6 @@ body:
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
@ -31,7 +30,6 @@ body:
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs windows`).
render: shell
validations:
required: true
- type: textarea

View file

@ -50,7 +50,7 @@ jobs:
labels: |
org.opencontainers.image.title=${{ vars.NAME }}
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@ -74,7 +74,7 @@ jobs:
context: .
push: true
provenance: false
platforms: linux/amd64,linux/arm64
platforms: linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}

View file

@ -1,5 +1,5 @@
FROM scratch
COPY --from=qemux/qemu-arm:7.12 / /
COPY --from=qemux/qemu-arm:2.21 / /
ARG VERSION_ARG="0.00"
ARG DEBCONF_NOWARNINGS="yes"
@ -9,14 +9,19 @@ ARG DEBCONF_NONINTERACTIVE_SEEN="true"
RUN set -eu && \
apt-get update && \
apt-get --no-install-recommends -y install \
bc \
jq \
curl \
7zip \
wsdd \
samba \
xz-utils \
wimtools \
dos2unix \
cabextract \
genisoimage \
libxml2-utils \
libarchive-tools \
netcat-openbsd && \
libarchive-tools && \
apt-get clean && \
echo "$VERSION_ARG" > /run/version && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@ -24,10 +29,11 @@ RUN set -eu && \
COPY --chmod=755 ./src /run/
COPY --chmod=755 ./assets /run/assets
ADD --chmod=664 https://github.com/qemus/virtiso-arm/releases/download/v0.1.271-1/virtio-win-0.1.271.tar.xz /var/drivers.txz
ADD --chmod=755 https://raw.githubusercontent.com/christgau/wsdd/v0.8/src/wsdd.py /usr/sbin/wsdd
ADD --chmod=664 https://github.com/qemus/virtiso-arm/releases/download/v0.1.262-2/virtio-win-0.1.262.tar.xz /drivers.txz
VOLUME /storage
EXPOSE 3389 8006
EXPOSE 8006 3389
ENV VERSION="11"
ENV RAM_SIZE="4G"

View file

@ -326,6 +326,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f</CommandLine>
<Description>Allow guest access to network shares</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -413,11 +418,6 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>20</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>21</Order>
<CommandLine>cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat"</CommandLine>
<Description>Execute custom script from the OEM folder if exists</Description>
</SynchronousCommand>

View file

@ -329,6 +329,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f</CommandLine>
<Description>Allow guest access to network shares</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -416,11 +421,6 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>20</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>21</Order>
<CommandLine>cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat"</CommandLine>
<Description>Execute custom script from the OEM folder if exists</Description>
</SynchronousCommand>

View file

@ -326,6 +326,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f</CommandLine>
<Description>Allow guest access to network shares</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>2</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -413,11 +418,6 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>20</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>21</Order>
<CommandLine>cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat"</CommandLine>
<Description>Execute custom script from the OEM folder if exists</Description>
</SynchronousCommand>

View file

@ -268,11 +268,6 @@
<Path>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\FirstNetwork" /v Category /t REG_DWORD /d 1 /f</Path>
<Description>Set Network Location to Home</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>26</Order>
<Path>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</Path>
<Description>Install VirtIO display driver</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
@ -355,6 +350,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "RequireSecuritySignature" /t REG_DWORD /d 0 /f</CommandLine>
<Description>Disable SMB signing requirement</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>4</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -447,13 +447,13 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>22</Order>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
<CommandLine>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</CommandLine>
<Description>Install VirtIO display driver</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>23</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>24</Order>

View file

@ -267,11 +267,6 @@
<Path>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\FirstNetwork" /v Category /t REG_DWORD /d 1 /f</Path>
<Description>Set Network Location to Home</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>26</Order>
<Path>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</Path>
<Description>Install VirtIO display driver</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
@ -354,6 +349,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "RequireSecuritySignature" /t REG_DWORD /d 0 /f</CommandLine>
<Description>Disable SMB signing requirement</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>4</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -446,13 +446,13 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>22</Order>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
<CommandLine>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</CommandLine>
<Description>Install VirtIO display driver</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>23</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>24</Order>

View file

@ -268,11 +268,6 @@
<Path>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\FirstNetwork" /v Category /t REG_DWORD /d 1 /f</Path>
<Description>Set Network Location to Home</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>26</Order>
<Path>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</Path>
<Description>Install VirtIO display driver</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
@ -355,6 +350,11 @@
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "RequireSecuritySignature" /t REG_DWORD /d 0 /f</CommandLine>
<Description>Disable SMB signing requirement</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>3</Order>
<CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
<Description>Allow RDP login with blank password</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>4</Order>
<CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
@ -447,13 +447,13 @@
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>22</Order>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
<CommandLine>pnputil -i -a C:\Windows\Drivers\viogpudo\viogpudo.inf</CommandLine>
<Description>Install VirtIO display driver</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>23</Order>
<CommandLine>cmd /C echo 20.20.20.1 host.lan >> %WINDIR%\system32\drivers\etc\hosts</CommandLine>
<Description>Add entry in hosts file</Description>
<CommandLine>cmd /C rd /q C:\Windows.old</CommandLine>
<Description>Remove empty Windows.old folder</Description>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<Order>24</Order>

View file

@ -6,14 +6,10 @@ services:
VERSION: "11"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 8006:8006
- 3389:3389/tcp
- 3389:3389/udp
volumes:
- ./windows:/storage
restart: always
stop_grace_period: 2m

View file

@ -1,100 +1,74 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: windows-pvc
spec:
accessModes:
- ReadWriteOnce
- ReadWriteOnce
resources:
requests:
storage: 64Gi
---
apiVersion: apps/v1
kind: Deployment
apiVersion: v1
kind: Pod
metadata:
name: windows
labels:
name: windows
spec:
replicas: 1
selector:
matchLabels:
app: windows
template:
metadata:
labels:
app: windows
spec:
containers:
- name: windows
image: dockurr/windows
env:
terminationGracePeriodSeconds: 120 # the Kubernetes default is 30 seconds and it may be not enough
containers:
- name: windows
image: dockurr/windows
ports:
- containerPort: 8006
protocol: TCP
- containerPort: 3389
protocol: TCP
- containerPort: 3389
protocol: UDP
securityContext:
privileged: true
env:
- name: VERSION
value: "11"
- name: RAM_SIZE
value: "4G"
- name: CPU_CORES
value: "2"
- name: DISK_SIZE
value: "64G"
ports:
- containerPort: 8006
name: http
protocol: TCP
- containerPort: 3389
name: rdp
protocol: TCP
- containerPort: 3389
name: udp
protocol: UDP
- containerPort: 5900
name: vnc
protocol: TCP
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
volumeMounts:
volumeMounts:
- mountPath: /storage
name: storage
- mountPath: /dev/kvm
name: dev-kvm
- mountPath: /dev/net/tun
name: dev-tun
terminationGracePeriodSeconds: 120
volumes:
- name: storage
persistentVolumeClaim:
claimName: windows-pvc
- hostPath:
path: /dev/kvm
name: dev-kvm
- hostPath:
path: /dev/net/tun
type: CharDevice
name: dev-tun
volumes:
- name: storage
persistentVolumeClaim:
claimName: windows-pvc
- name: dev-kvm
hostPath:
path: /dev/kvm
---
apiVersion: v1
kind: Service
metadata:
name: windows
spec:
internalTrafficPolicy: Cluster
ports:
- name: http
port: 8006
protocol: TCP
targetPort: 8006
- name: rdp
port: 3389
protocol: TCP
targetPort: 3389
- name: udp
port: 3389
protocol: UDP
targetPort: 3389
- name: vnc
port: 5900
protocol: TCP
targetPort: 5900
type: NodePort
selector:
app: windows
type: ClusterIP
name: windows
ports:
- name: tcp-8006
protocol: TCP
port: 8006
targetPort: 8006
- name: tcp-3389
protocol: TCP
port: 3389
targetPort: 3389
- name: udp-3389
protocol: UDP
port: 3389
targetPort: 3389

188
readme.md
View file

@ -26,7 +26,7 @@ Windows for ARM in a Docker container, for devices like the Raspberry Pi 5 and m
## Usage 🐳
##### Via Docker Compose:
Via Docker Compose:
```yaml
services:
@ -37,34 +37,34 @@ services:
VERSION: "11"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 8006:8006
- 3389:3389/tcp
- 3389:3389/udp
volumes:
- ./windows:/storage
restart: always
stop_grace_period: 2m
```
##### Via Docker CLI:
Via Docker CLI:
```bash
docker run -it --rm --name windows -p 8006:8006 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/windows:/storage" --stop-timeout 120 dockurr/windows
docker run -it --rm -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 120 dockurr/windows
```
##### Via Kubernetes:
Via Kubernetes:
```shell
kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/master/kubernetes.yml
kubectl apply -f https://raw.githubusercontent.com/dockur/windows-arm/refs/heads/master/kubernetes.yml
```
##### Via Github Codespaces:
## Compatibility ⚙️
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/dockur/windows-arm)
| **Product** | **Platform** | |
|---|---|---|
| Docker Engine | Linux| ✅ |
| Docker Desktop | Linux | ❌ |
| Docker Desktop | macOS | ❌ |
## FAQ 💬
@ -72,7 +72,7 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
Very simple! These are the steps:
- Start the container and connect to [port 8006](http://127.0.0.1:8006/) using your web browser.
- Start the container and connect to [port 8006](http://localhost:8006) using your web browser.
- Sit back and relax while the magic happens, the whole installation will be performed fully automatic.
@ -82,7 +82,7 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
### How do I select the Windows version?
By default, Windows 11 Pro will be installed. But you can add the `VERSION` environment variable to your compose file, in order to specify an alternative Windows version to be downloaded:
By default, Windows 11 will be installed. But you can add the `VERSION` environment variable to your compose file, in order to specify an alternative Windows version to be downloaded:
```yaml
environment:
@ -91,15 +91,15 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
Select from the values below:
| **Value** | **Version** | **Size** |
|---|---|---|
| `11` | Windows 11 Pro | 4.8 GB |
| `11l` | Windows 11 LTSC | 4.7 GB |
| `11e` | Windows 11 Enterprise | 4.8 GB |
||||
| `10` | Windows 10 Pro | 3.5 GB |
| `10l` | Windows 10 LTSC | 4.1 GB |
| `10e` | Windows 10 Enterprise | 3.4 GB |
| **Value** | **Version** | **Platform** | **Size** |
|---|---|---|---|
| `11` | Windows 11 Pro | ARM64 | 5.0 GB |
| `11l` | Windows 11 LTSC | ARM64 | 3.9 GB |
| `11e` | Windows 11 Enterprise | ARM64 | 4.8 GB |
|||||
| `10` | Windows 10 Pro | ARM64 | 3.5 GB |
| `10l` | Windows 10 LTSC | ARM64 | 4.1 GB |
| `10e` | Windows 10 Enterprise | ARM64 | 3.4 GB |
> [!TIP]
> To install x64 versions of Windows, use [dockur/windows](https://github.com/dockur/windows/).
@ -110,10 +110,10 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
```yaml
volumes:
- ./windows:/storage
- /var/win:/storage
```
Replace the example path `./windows` with the desired storage folder or named volume.
Replace the example path `/var/win` with the desired storage folder.
### How do I change the size of the disk?
@ -131,29 +131,69 @@ kubectl apply -f https://raw.githubusercontent.com/dockur/windows/refs/heads/mas
The display output is a simple framebuffer, just so that the screen can be visible during installation as it doesn't require any drivers.
To add a virtual graphics cards to your machine that allows for higher resolutions, you can add the following to your compose file after Windows is fully installed:
After Windows is fully installed, you can add the following to your compose file:
```yaml
environment:
VGA: "virtio-gpu"
```
to add a virtual graphics cards to your machine that allows for higher resolutions.
### How do I share files with the host?
Open 'File Explorer' and click on the 'Network' section, you will see a computer called `host.lan`.
Double-click it and it will show a folder called `Data`, which can be bound to any folder on your host via the compose file:
Open 'File Explorer' and click on the 'Network' section, you will see a computer called `host.lan`. Double-click it and it will show a folder called `Data`, which can be bound to any folder on your host via the compose file:
```yaml
volumes:
- ./example:/data
- /home/user/example:/data
```
The example folder `./example` will be available as ` \\host.lan\Data`.
The example folder `/home/user/example` will be available as ` \\host.lan\Data`.
> [!TIP]
> You can map this path to a drive letter in Windows, for easier access.
### How do I install a custom image?
In order to download an unsupported ISO image that is not selectable from the list above, specify the URL of that ISO in the `VERSION` environment variable, for example:
```yaml
environment:
VERSION: "https://example.com/win.iso"
```
Alternatively, you can also skip the download and use a local file instead, by binding it in your compose file in this way:
```yaml
volumes:
- /home/user/example.iso:/custom.iso
```
Replace the example path `/home/user/example.iso` with the filename of your desired ISO file, the value of `VERSION` will be ignored in this case.
### How do I run a script after installation?
To run your own script after installation, you can create a file called `install.bat` and place it in a folder together with any additional files it needs (software to be installed for example). Then bind that folder in your compose file like this:
```yaml
volumes:
- /home/user/example:/oem
```
The example folder `/home/user/example` will be copied to `C:\OEM` during installation and the containing `install.bat` will be executed during the last step.
### How do I perform a manual installation?
It's best to stick to the automatic installation, as it adjusts various settings to prevent common issues when running Windows inside a virtual environment.
However, if you insist on performing the installation manually, add the following environment variable to your compose file:
```yaml
environment:
MANUAL: "Y"
```
### How do I change the amount of CPU or RAM?
By default, the container will be allowed to use a maximum of 2 CPU cores and 4 GB of RAM.
@ -168,9 +208,9 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
### How do I configure the username and password?
By default, a user called `Docker` is created during installation and its password is `admin`.
By default, a user called `Docker` is created during the installation, with an empty password.
If you want to use different credentials, you can configure them in your compose file (only before installation):
If you want to use different credentials, you can change them in your compose file:
```yaml
environment:
@ -180,9 +220,7 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
### How do I select the Windows language?
By default, the English version of Windows will be downloaded.
But before installation you can add the `LANGUAGE` environment variable to your compose file, in order to specify an alternative language:
By default, the English version of Windows will be downloaded. But you can add the `LANGUAGE` environment variable to your compose file, in order to specify an alternative language:
```yaml
environment:
@ -193,7 +231,7 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
### How do I select the keyboard layout?
If you want to use a keyboard layout or locale that is not the default for your selected language, you can add `KEYBOARD` and `REGION` variables like this (before installation):
If you want to use a keyboard layout or locale that is not the default for your selected language, you can add the `KEYBOARD` and `REGION` variables with a culture code, like this:
```yaml
environment:
@ -201,53 +239,14 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
KEYBOARD: "en-US"
```
### How do I install a custom image?
In order to download an unsupported ISO image, specify its URL in the `VERSION` environment variable:
```yaml
environment:
VERSION: "https://example.com/win.iso"
```
Alternatively, you can also skip the download and use a local file instead, by binding it in your compose file in this way:
```yaml
volumes:
- ./example.iso:/boot.iso
```
Replace the example path `./example.iso` with the filename of your desired ISO file. The value of `VERSION` will be ignored in this case.
### How do I run a script after installation?
To run your own script after installation, you can create a file called `install.bat` and place it in a folder together with any additional files it needs (software to be installed for example).
Then bind that folder in your compose file like this:
```yaml
volumes:
- ./example:/oem
```
The example folder `./example` will be copied to `C:\OEM` and the containing `install.bat` will be executed during the last step of the automatic installation.
### How do I perform a manual installation?
It's recommended to stick to the automatic installation, as it adjusts various settings to prevent common issues when running Windows inside a virtual environment.
However, if you insist on performing the installation manually at your own risk, add the following environment variable to your compose file:
```yaml
environment:
MANUAL: "Y"
```
> [!NOTE]
> Changing these values will have no effect after the installation has been performed already. Use the control panel inside Windows in that case.
### How do I connect using RDP?
The web-viewer is mainly meant to be used during installation, as its picture quality is low, and it has no audio or clipboard for example.
So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `Docker` and password `admin`.
So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `Docker` and by leaving the password empty.
There is a RDP client for [Android](https://play.google.com/store/apps/details?id=com.microsoft.rdc.androidx) available from the Play Store and one for [iOS](https://apps.apple.com/nl/app/microsoft-remote-desktop/id714464092?l=en-GB) in the Apple Store. For Linux you can use [FreeRDP](https://www.freerdp.com/) and on Windows just type `mstsc` in the search box.
@ -292,7 +291,7 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
After configuring the container for [macvlan](#how-do-i-assign-an-individual-ip-address-to-the-container), it is possible for Windows to become part of your home network by requesting an IP from your router, just like a real PC.
To enable this mode, in which the container and Windows will have separate IP addresses, add the following lines to your compose file:
To enable this mode, add the following lines to your compose file:
```yaml
environment:
@ -303,6 +302,9 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
- 'c *:* rwm'
```
> [!NOTE]
> In this mode, the container and Windows will each have their own separate IPs.
### How do I add multiple disks?
To create additional disks, modify your compose file like this:
@ -312,18 +314,18 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- ./example2:/storage2
- ./example3:/storage3
- /home/example:/storage2
- /mnt/data/example:/storage3
```
### How do I pass-through a disk?
It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
It is possible to pass-through disk devices directly by adding them to your compose file in this way:
```yaml
devices:
- /dev/sdb:/disk1
- /dev/sdc1:/disk2
- /dev/sdc:/disk2
```
Use `/disk1` if you want it to become your main drive (which will be formatted during installation), and use `/disk2` and higher to add them as secondary drives (which will stay untouched).
@ -339,20 +341,14 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
- /dev/bus/usb
```
If the device is a USB disk drive, please wait until after the installation is completed before connecting it. Otherwise the installation may fail, as the order of the disks can get rearranged.
> [!IMPORTANT]
> If the device is a USB disk drive, please wait until after the installation is completed before connecting it. Otherwise the installation may fail, as the order of the disks can get rearranged.
### How do I verify if my system supports KVM?
First check if your software is compatible using this chart:
Only Linux and Windows 11 support KVM virtualization, macOS and Windows 10 do not unfortunately.
| **Product** | **Linux** | **Win11** | **Win10** | **macOS** |
|---|---|---|---|---|
| Docker CLI | ✅ | ✅ | ❌ | ❌ |
| Docker Desktop | ❌ | ✅ | ❌ | ❌ |
| Podman CLI | ✅ | ✅ | ❌ | ❌ |
| Podman Desktop | ✅ | ✅ | ❌ | ❌ |
After that you can run the following commands in Linux to check your system:
You can run the following commands in Linux to check your system:
```bash
sudo apt install cpu-checker
@ -367,11 +363,11 @@ The example folder `./example` will be available as ` \\host.lan\Data`.
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
If you did not receive any error from `kvm-ok` but the container still complains about a missing KVM device, it could help to add `privileged: true` to your compose file (or `sudo` to your `docker` command) to rule out any permission issue.
If you do not receive any error from `kvm-ok` but the container still complains about KVM, please check whether:
### How do I run a Linux desktop in a container?
- you are not using "Docker Desktop for Linux" as it does not support KVM, instead make use of Docker Engine directly.
You can use [qemus/qemu-arm](https://github.com/qemus/qemu-arm) in that case.
- it could help to add `privileged: true` to your compose file (or `sudo` to your `docker run` command), to rule out any permission issue.
### Is this project legal?

View file

@ -1,12 +1,10 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${KEY:=""}"
: "${WIDTH:=""}"
: "${HEIGHT:=""}"
: "${VERIFY:=""}"
: "${REGION:=""}"
: "${EDITION:=""}"
: "${MANUAL:=""}"
: "${REMOVE:=""}"
: "${VERSION:=""}"
@ -17,6 +15,7 @@ set -Eeuo pipefail
: "${PASSWORD:=""}"
MIRRORS=2
PLATFORM="ARM64"
parseVersion() {
@ -61,23 +60,20 @@ parseVersion() {
"8e" | "81e" | "8.1e" | "win8e" | "win81e" | "windows 8e" )
error "Windows 8 $msg" && return 1
;;
"7" | "win7" | "windows7" | "windows 7" | "7u" | "win7u" | "windows7u" | "windows 7u" | "7e" | \
"win7e" | "windows7e" | "windows 7e" | "7x86" | "win7x86" | "win732" | "windows7x86" | "7ux86" | \
"7u32" | "win7x86-ultimate" | "7ex86" | "7e32" | "win7x86-enterprise" )
"7" | "7e" | "win7" | "win7e" | "windows7" | "windows 7" | \
"7u" | "win7u" | "windows7u" | "windows 7u" | \
"7x86" | "win7x86" | "windows7x86" | "win7x86-enterprise" )
error "Windows 7 $msg" && return 1
;;
"vista" | "vs" | "6" | "winvista" | "windowsvista" | "windows vista" | "vistu" | "vu" | "6u" | "winvistu" | \
"viste" | "ve" | "6e" | "winviste" | "vistax86" | "vista32" | "6x86" | "winvistax86" | "windowsvistax86" | \
"vux86" | "vu32" | "winvistax86-ultimate" | "vex86" | "ve32" | "winvistax86-enterprise" )
"vista" | "ve" | "6" | "winvista" | "windowsvista" | "windows vista" | \
"vistu" | "vu" | "6u" | "winvistu" | "windowsvistu" | "windows vistu" | \
"vistax86" | "vex86" | "6x86" | "winvistax86" | "windowsvistax86" | "winvistax86-enterprise" )
error "Windows Vista $msg" && return 1
;;
"xp" | "xp32" | "xpx86" | "5" | "5x86" | "winxp" | "winxp86" | "windowsxp" | "windows xp" | \
"xp64" | "xpx64" | "5x64" | "winxp64" | "winxpx64" | "windowsxp64" | "windowsxpx64" )
error "Windows XP $msg" && return 1
;;
"2k" | "2000" | "win2k" | "win2000" | "windows2k" | "windows2000" )
error "Windows 2000 $msg" && return 1
;;
"25" | "2025" | "win25" | "win2025" | "windows2025" | "windows 2025" )
error "Windows Server 2025 $msg" && return 1
;;
@ -90,9 +86,6 @@ parseVersion() {
"16" | "2016" | "win16" | "win2016" | "windows2016" | "windows 2016" )
error "Windows Server 2016 $msg" && return 1
;;
"hv" | "hyperv" | "hyper v" | "hyper-v" | "19hv" | "2019hv" | "win2019hv" )
error "Hyper-V Server 2019 $msg" && return 1
;;
"2012" | "2012r2" | "win2012" | "win2012r2" | "windows2012" | "windows 2012" )
error "Windows Server 2012 $msg" && return 1
;;
@ -102,13 +95,11 @@ parseVersion() {
"2003" | "2003r2" | "win2003" | "win2003r2" | "windows2003" | "windows 2003" )
error "Windows Server 2003 $msg" && return 1
;;
"tiny11" | "tiny 11" )
VERSION="tiny11"
[ -z "$DETECTED" ] && DETECTED="win11arm64"
;;
"core11" | "core 11" )
VERSION="core11"
[ -z "$DETECTED" ] && DETECTED="win11arm64"
error "Tiny 11 Core $msg" && return 1
;;
"tiny11" | "tiny 11" )
error "Tiny 11 $msg" && return 1
;;
"tiny10" | "tiny 10" )
error "Tiny 10 $msg" && return 1
@ -124,202 +115,162 @@ getLanguage() {
local ret="$2"
local lang=""
local desc=""
local short=""
local culture=""
case "${id,,}" in
"ar" | "ar-"* )
short="ar"
lang="Arabic"
desc="$lang"
culture="ar-SA" ;;
"bg" | "bg-"* )
short="bg"
lang="Bulgarian"
desc="$lang"
culture="bg-BG" ;;
"cs" | "cs-"* | "cz" | "cz-"* )
short="cs"
lang="Czech"
desc="$lang"
culture="cs-CZ" ;;
"da" | "da-"* | "dk" | "dk-"* )
short="da"
lang="Danish"
desc="$lang"
culture="da-DK" ;;
"de" | "de-"* )
short="de"
lang="German"
desc="$lang"
culture="de-DE" ;;
"el" | "el-"* | "gr" | "gr-"* )
short="el"
lang="Greek"
desc="$lang"
culture="el-GR" ;;
"gb" | "en-gb" )
short="en-gb"
lang="English International"
desc="English"
culture="en-GB" ;;
"en" | "en-"* )
short="en"
lang="English"
desc="English"
culture="en-US" ;;
"mx" | "es-mx" )
short="mx"
lang="Spanish (Mexico)"
desc="Spanish"
culture="es-MX" ;;
"es" | "es-"* )
short="es"
lang="Spanish"
desc="$lang"
culture="es-ES" ;;
"et" | "et-"* )
short="et"
lang="Estonian"
desc="$lang"
culture="et-EE" ;;
"fi" | "fi-"* )
short="fi"
lang="Finnish"
desc="$lang"
culture="fi-FI" ;;
"ca" | "fr-ca" )
short="ca"
lang="French Canadian"
desc="French"
culture="fr-CA" ;;
"fr" | "fr-"* )
short="fr"
lang="French"
desc="$lang"
culture="fr-FR" ;;
"he" | "he-"* | "il" | "il-"* )
short="he"
lang="Hebrew"
desc="$lang"
culture="he-IL" ;;
"hr" | "hr-"* | "cr" | "cr-"* )
short="hr"
lang="Croatian"
desc="$lang"
culture="hr-HR" ;;
"hu" | "hu-"* )
short="hu"
lang="Hungarian"
desc="$lang"
culture="hu-HU" ;;
"it" | "it-"* )
short="it"
lang="Italian"
desc="$lang"
culture="it-IT" ;;
"ja" | "ja-"* | "jp" | "jp-"* )
short="ja"
lang="Japanese"
desc="$lang"
culture="ja-JP" ;;
"ko" | "ko-"* | "kr" | "kr-"* )
short="ko"
lang="Korean"
desc="$lang"
culture="ko-KR" ;;
"lt" | "lt-"* )
short="lt"
lang="Lithuanian"
desc="$lang"
culture="lt-LT" ;;
culture="lv-LV" ;;
"lv" | "lv-"* )
short="lv"
lang="Latvian"
desc="$lang"
culture="lv-LV" ;;
culture="lt-LT" ;;
"nb" | "nb-"* |"nn" | "nn-"* | "no" | "no-"* )
short="no"
lang="Norwegian"
desc="$lang"
culture="nb-NO" ;;
"nl" | "nl-"* )
short="nl"
lang="Dutch"
desc="$lang"
culture="nl-NL" ;;
"pl" | "pl-"* )
short="pl"
lang="Polish"
desc="$lang"
culture="pl-PL" ;;
"br" | "pt-br" )
short="pt"
lang="Brazilian Portuguese"
desc="Portuguese"
culture="pt-BR" ;;
"pt" | "pt-"* )
short="pp"
lang="Portuguese"
desc="$lang"
culture="pt-BR" ;;
"ro" | "ro-"* )
short="ro"
lang="Romanian"
desc="$lang"
culture="ro-RO" ;;
"ru" | "ru-"* )
short="ru"
lang="Russian"
desc="$lang"
culture="ru-RU" ;;
"sk" | "sk-"* )
short="sk"
lang="Slovak"
desc="$lang"
culture="sk-SK" ;;
"sl" | "sl-"* | "si" | "si-"* )
short="sl"
lang="Slovenian"
desc="$lang"
culture="sl-SI" ;;
"sr" | "sr-"* )
short="sr"
lang="Serbian Latin"
desc="Serbian"
culture="sr-Latn-RS" ;;
"sv" | "sv-"* | "se" | "se-"* )
short="sv"
lang="Swedish"
desc="$lang"
culture="sv-SE" ;;
"th" | "th-"* )
short="th"
lang="Thai"
desc="$lang"
culture="th-TH" ;;
"tr" | "tr-"* )
short="tr"
lang="Turkish"
desc="$lang"
culture="tr-TR" ;;
"ua" | "ua-"* | "uk" | "uk-"* )
short="uk"
lang="Ukrainian"
desc="$lang"
culture="uk-UA" ;;
"hk" | "zh-hk" | "cn-hk" )
short="hk"
lang="Chinese (Traditional)"
desc="Chinese HK"
culture="zh-TW" ;;
"tw" | "zh-tw" | "cn-tw" )
short="tw"
lang="Chinese (Traditional)"
desc="Chinese TW"
culture="zh-TW" ;;
"zh" | "zh-"* | "cn" | "cn-"* )
short="cn"
lang="Chinese (Simplified)"
desc="Chinese"
culture="zh-CN" ;;
@ -328,7 +279,6 @@ getLanguage() {
case "${ret,,}" in
"desc" ) echo "$desc" ;;
"name" ) echo "$lang" ;;
"code" ) echo "$short" ;;
"culture" ) echo "$culture" ;;
*) echo "$desc";;
esac
@ -395,8 +345,6 @@ printVersion() {
local desc="$2"
case "${id,,}" in
"tiny11"* ) desc="Tiny 11" ;;
"core11"* ) desc="Core 11" ;;
"win10"* ) desc="Windows 10" ;;
"win11"* ) desc="Windows 11" ;;
esac
@ -451,10 +399,7 @@ fromFile() {
local file="${1,,}"
local arch="${PLATFORM,,}"
file="${file//-/_}"
file="${file// /_}"
case "$file" in
case "${file// /_}" in
*"_x64_"* | *"_x64."*)
arch="x64"
;;
@ -466,13 +411,7 @@ fromFile() {
;;
esac
case "$file" in
"tiny11core"* | "tiny11_core"* | "tiny_11_core"* )
id="core11"
;;
"tiny11"* | "tiny_11"* )
id="tiny11"
;;
case "${file// /_}" in
"win10"*| "win_10"* | *"windows10"* | *"windows_10"* )
id="win10${arch}"
;;
@ -497,9 +436,7 @@ fromName() {
case "${name,,}" in
*"windows 10"* ) id="win10${arch}" ;;
*"optimum 10"* ) id="win10${arch}" ;;
*"windows 11"* ) id="win11${arch}" ;;
*"optimum 11"* ) id="win11${arch}" ;;
esac
echo "$id"
@ -550,7 +487,6 @@ getMido() {
local id="$1"
local lang="$2"
local ret="$3"
local url=""
local sum=""
local size=""
@ -562,15 +498,15 @@ getMido() {
sum="57d1dfb2c6690a99fe99226540333c6c97d3fd2b557a50dfe3d68c3f675ef2b0"
;;
"win11arm64-enterprise-ltsc-eval" )
size=5042194432
sum="3dcdba9c9c0aa0430d4332b60c9afcb3cd613d648a49cbba2d4ef7b5978f32e8"
size=4252764160
sum="ccec358a760c3c581249f091ed42d04f37b2b99c347b7a58257c3cc272d7982c"
;;
esac
case "${ret,,}" in
"sum" ) echo "$sum" ;;
"size" ) echo "$size" ;;
*) echo "$url";;
*) echo "";;
esac
return 0
@ -586,30 +522,157 @@ getLink1() {
local url=""
local sum=""
local size=""
local host="https://dl.bobpony.com/windows"
local host="https://drive.massgrave.dev"
[[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
culture=$(getLanguage "$lang" "culture")
case "${id,,}" in
"win11arm64" | "win11arm64-enterprise" | "win11arm64-enterprise-eval" )
size=5219411968
sum="dbd54452c3c20b4625f511dae3c3e057270448fb661232d4fa66279f59a63157"
url="11/en-us_windows_11_24h2_arm64.iso"
"win11arm64" )
case "${culture,,}" in
"ar" | "ar-"* ) url="ar-sa_windows_11_consumer_editions_version_24h2_arm64_dvd_331e352e.iso" ;;
"bg" | "bg-"* ) url="bg-bg_windows_11_consumer_editions_version_24h2_arm64_dvd_ad0f4159.iso" ;;
"cs" | "cs-"* ) url="cs-cz_windows_11_consumer_editions_version_24h2_arm64_dvd_09e9a65d.iso" ;;
"da" | "da-"* ) url="da-dk_windows_11_consumer_editions_version_24h2_arm64_dvd_cfc9f7d2.iso" ;;
"de" | "de-"* ) url="de-de_windows_11_consumer_editions_version_24h2_arm64_dvd_77817e34.iso" ;;
"el" | "el-"* ) url="el-gr_windows_11_consumer_editions_version_24h2_arm64_dvd_7ff49fe0.iso" ;;
"gb" | "en-gb" ) url="en-gb_windows_11_consumer_editions_version_24h2_arm64_dvd_2fb276c5.iso" ;;
"en" | "en-"* )
size=5460387840
sum="57d1dfb2c6690a99fe99226540333c6c97d3fd2b557a50dfe3d68c3f675ef2b0"
url="en-us_windows_11_consumer_editions_version_24h2_arm64_dvd_4cc70bf6.iso" ;;
"mx" | "es-mx" ) url="es-mx_windows_11_consumer_editions_version_24h2_arm64_dvd_29df5eac.iso" ;;
"es" | "es-"* ) url="es-es_windows_11_consumer_editions_version_24h2_arm64_dvd_b0586d9e.iso" ;;
"et" | "et-"* ) url="et-ee_windows_11_consumer_editions_version_24h2_arm64_dvd_0895b4c2.iso" ;;
"fi" | "fi-"* ) url="fi-fi_windows_11_consumer_editions_version_24h2_arm64_dvd_030941fb.iso" ;;
"ca" | "fr-ca" ) url="fr-ca_windows_11_consumer_editions_version_24h2_arm64_dvd_08e2b30e.iso" ;;
"fr" | "fr-"* ) url="fr-fr_windows_11_consumer_editions_version_24h2_arm64_dvd_f7d51069.iso" ;;
"he" | "he-"* ) url="he-il_windows_11_consumer_editions_version_24h2_arm64_dvd_24838d6b.iso" ;;
"hr" | "hr-"* ) url="hr-hr_windows_11_consumer_editions_version_24h2_arm64_dvd_260e2f8a.iso" ;;
"hu" | "hu-"* ) url="hu-hu_windows_11_consumer_editions_version_24h2_arm64_dvd_981626d6.iso" ;;
"it" | "it-"* ) url="it-it_windows_11_consumer_editions_version_24h2_arm64_dvd_30f0547d.iso" ;;
"ja" | "ja-"* ) url="ja-jp_windows_11_consumer_editions_version_24h2_arm64_dvd_f5fc9b3e.iso" ;;
"ko" | "ko-"* ) url="ko-kr_windows_11_consumer_editions_version_24h2_arm64_dvd_5dddc52e.iso" ;;
"lt" | "lt-"* ) url="lt-lt_windows_11_consumer_editions_version_24h2_arm64_dvd_e83cfe4c.iso" ;;
"lv" | "lv-"* ) url="lv-lv_windows_11_consumer_editions_version_24h2_arm64_dvd_0028b5e8.iso" ;;
"nb" | "nb-"* ) url="nb-no_windows_11_consumer_editions_version_24h2_arm64_dvd_1df6aff1.iso" ;;
"nl" | "nl-"* ) url="nl-nl_windows_11_consumer_editions_version_24h2_arm64_dvd_2a794237.iso" ;;
"pl" | "pl-"* ) url="pl-pl_windows_11_consumer_editions_version_24h2_arm64_dvd_80af0fd7.iso" ;;
"br" | "pt-br" ) url="pt-br_windows_11_consumer_editions_version_24h2_arm64_dvd_0648fb13.iso" ;;
"pt" | "pt-"* ) url="pt-pt_windows_11_consumer_editions_version_24h2_arm64_dvd_223f3f3a.iso" ;;
"ro" | "ro-"* ) url="ro-ro_windows_11_consumer_editions_version_24h2_arm64_dvd_78f3a92a.iso" ;;
"ru" | "ru-"* ) url="ru-ru_windows_11_consumer_editions_version_24h2_arm64_dvd_8e4d44aa.iso" ;;
"sk" | "sk-"* ) url="sk-sk_windows_11_consumer_editions_version_24h2_arm64_dvd_c8c147d6.iso" ;;
"sl" | "sl-"* ) url="sl-si_windows_11_consumer_editions_version_24h2_arm64_dvd_f4e5671d.iso" ;;
"sr" | "sr-"* ) url="sr-latn-rs_windows_11_consumer_editions_version_24h2_arm64_dvd_489179df.iso" ;;
"sv" | "sv-"* ) url="sv-se_windows_11_consumer_editions_version_24h2_arm64_dvd_fa42c0cc.iso" ;;
"th" | "th-"* ) url="th-th_windows_11_consumer_editions_version_24h2_arm64_dvd_abb88faa.iso" ;;
"tr" | "tr-"* ) url="tr-tr_windows_11_consumer_editions_version_24h2_arm64_dvd_ea2494b5.iso" ;;
"uk" | "uk-"* ) url="uk-ua_windows_11_consumer_editions_version_24h2_arm64_dvd_70fa6caf.iso" ;;
"zh-hk" | "zh-tw" ) url="zh-tw_windows_11_consumer_editions_version_24h2_arm64_dvd_fa6b02d0.iso" ;;
"zh" | "zh-"* ) url="zh-cn_windows_11_consumer_editions_version_24h2_arm64_dvd_4b5c8070.iso" ;;
esac
;;
"win11arm64-enterprise" | "win11arm64-enterprise-eval" )
case "${culture,,}" in
"ar" | "ar-"* ) url="ar-sa_windows_11_business_editions_version_24h2_arm64_dvd_0b673385.iso" ;;
"bg" | "bg-"* ) url="bg-bg_windows_11_business_editions_version_24h2_arm64_dvd_788c03e0.iso" ;;
"cs" | "cs-"* ) url="cs-cz_windows_11_business_editions_version_24h2_arm64_dvd_1f7bc350.iso" ;;
"da" | "da-"* ) url="da-dk_windows_11_business_editions_version_24h2_arm64_dvd_9d466587.iso" ;;
"de" | "de-"* ) url="de-de_windows_11_business_editions_version_24h2_arm64_dvd_c2e28d02.iso" ;;
"el" | "el-"* ) url="el-gr_windows_11_business_editions_version_24h2_arm64_dvd_549d89e0.iso" ;;
"gb" | "en-gb" ) url="en-gb_windows_11_business_editions_version_24h2_arm64_dvd_4b053a98.iso" ;;
"en" | "en-"* )
size=5388951552
sum="15ff94a99e89846c54316275f60ea697c9517e5dea7b3a963157a4c632524f72"
url="en-us_windows_11_business_editions_version_24h2_arm64_dvd_ad92e9d8.iso" ;;
"mx" | "es-mx" ) url="es-mx_windows_11_business_editions_version_24h2_arm64_dvd_2f7a4f0e.iso" ;;
"es" | "es-"* ) url="es-es_windows_11_business_editions_version_24h2_arm64_dvd_81ab0494.iso" ;;
"et" | "et-"* ) url="et-ee_windows_11_business_editions_version_24h2_arm64_dvd_49e9e4b9.iso" ;;
"fi" | "fi-"* ) url="fi-fi_windows_11_business_editions_version_24h2_arm64_dvd_18c415d6.iso" ;;
"ca" | "fr-ca" ) url="fr-ca_windows_11_business_editions_version_24h2_arm64_dvd_9de095b9.iso" ;;
"fr" | "fr-"* ) url="fr-fr_windows_11_business_editions_version_24h2_arm64_dvd_dadf21c7.iso" ;;
"he" | "he-"* ) url="he-il_windows_11_business_editions_version_24h2_arm64_dvd_a836dc29.iso" ;;
"hr" | "hr-"* ) url="hr-hr_windows_11_business_editions_version_24h2_arm64_dvd_8b68a3fd.iso" ;;
"hu" | "hu-"* ) url="hu-hu_windows_11_business_editions_version_24h2_arm64_dvd_0e1fd665.iso" ;;
"it" | "it-"* ) url="it-it_windows_11_business_editions_version_24h2_arm64_dvd_c4e7511f.iso" ;;
"ja" | "ja-"* ) url="ja-jp_windows_11_business_editions_version_24h2_arm64_dvd_28e70c96.iso" ;;
"ko" | "ko-"* ) url="ko-kr_windows_11_business_editions_version_24h2_arm64_dvd_739157b9.iso" ;;
"lt" | "lt-"* ) url="lt-lt_windows_11_business_editions_version_24h2_arm64_dvd_896253e8.iso" ;;
"lv" | "lv-"* ) url="lv-lv_windows_11_business_editions_version_24h2_arm64_dvd_e5fd8399.iso" ;;
"nb" | "nb-"* ) url="nb-no_windows_11_business_editions_version_24h2_arm64_dvd_698e7791.iso" ;;
"nl" | "nl-"* ) url="nl-nl_windows_11_business_editions_version_24h2_arm64_dvd_7e6e0919.iso" ;;
"pl" | "pl-"* ) url="pl-pl_windows_11_business_editions_version_24h2_arm64_dvd_b297967a.iso" ;;
"br" | "pt-br" ) url="pt-br_windows_11_business_editions_version_24h2_arm64_dvd_7011721a.iso" ;;
"pt" | "pt-"* ) url="pt-pt_windows_11_business_editions_version_24h2_arm64_dvd_221e64a5.iso" ;;
"ro" | "ro-"* ) url="ro-ro_windows_11_business_editions_version_24h2_arm64_dvd_82cbceb3.iso" ;;
"ru" | "ru-"* ) url="ru-ru_windows_11_business_editions_version_24h2_arm64_dvd_6ab7f1a4.iso" ;;
"sk" | "sk-"* ) url="sk-sk_windows_11_business_editions_version_24h2_arm64_dvd_04ce533b.iso" ;;
"sl" | "sl-"* ) url="sl-si_windows_11_business_editions_version_24h2_arm64_dvd_dd345ed1.iso" ;;
"sr" | "sr-"* ) url="sr-latn-rs_windows_11_business_editions_version_24h2_arm64_dvd_f2b86976.iso" ;;
"sv" | "sv-"* ) url="sv-se_windows_11_business_editions_version_24h2_arm64_dvd_88f0e36f.iso" ;;
"th" | "th-"* ) url="th-th_windows_11_business_editions_version_24h2_arm64_dvd_9c714026.iso" ;;
"tr" | "tr-"* ) url="tr-tr_windows_11_business_editions_version_24h2_arm64_dvd_4da6f82d.iso" ;;
"uk" | "uk-"* ) url="uk-ua_windows_11_business_editions_version_24h2_arm64_dvd_65304891.iso" ;;
"zh-hk" | "zh-tw" ) url="zh-tw_windows_11_business_editions_version_24h2_arm64_dvd_99a82a4f.iso" ;;
"zh" | "zh-"* ) url="zh-cn_windows_11_business_editions_version_24h2_arm64_dvd_9696a5e8.iso" ;;
esac
;;
"win11arm64-ltsc" | "win11arm64-enterprise-ltsc-eval" )
[[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
size=5121449984
sum="f8f068cdc90c894a55d8c8530db7c193234ba57bb11d33b71383839ac41246b4"
url="11/X23-81950_26100.1742.240906-0331.ge_release_svc_refresh_CLIENT_ENTERPRISES_OEM_A64FRE_en-us.iso"
url="en-us_windows_11_iot_enterprise_ltsc_2024_arm64_dvd_ec517836.iso"
;;
"win10arm64" | "win10arm64-enterprise" | "win10arm64-enterprise-eval" )
size=4689637376
sum="7b43e64f4e3b961a83f9b70efa4b9d863bc5c348fe86d75917ac974116d17227"
url="10/en-us_windows_10_22h2_arm64.iso"
case "${culture,,}" in
"ar" | "ar-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Arabic_Pro_Ent_EDU_N_MLF_X23-67213.ISO" ;;
"bg" | "bg-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Bulgarian_Pro_Ent_EDU_N_MLF_X23-67215.ISO" ;;
"cs" | "cs-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Czech_Pro_Ent_EDU_N_MLF_X23-67219.ISO" ;;
"da" | "da-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Danish_Pro_Ent_EDU_N_MLF_X23-67220.ISO" ;;
"de" | "de-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_German_Pro_Ent_EDU_N_MLF_X23-67228.ISO" ;;
"el" | "el-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Greek_Pro_Ent_EDU_N_MLF_X23-67229.ISO" ;;
"gb" | "en-gb" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Eng_Intl_Pro_Ent_EDU_N_MLF_X23-67222.ISO" ;;
"en" | "en-"* )
size=5190453248
sum="bd96b342193f81c0a2e6595d8d8b8dc01dbf789d19211699f6299fec7b712197"
url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_English_Pro_Ent_EDU_N_MLF_X23-67223.ISO" ;;
"mx" | "es-mx" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Spanish_Latam_Pro_Ent_EDU_N_MLF_X23-67245.ISO" ;;
"es" | "es-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Spanish_Pro_Ent_EDU_N_MLF_X23-67246.ISO" ;;
"et" | "et-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Estonian_Pro_Ent_EDU_N_MLF_X23-67224.ISO" ;;
"fi" | "fi-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Finnish_Pro_Ent_EDU_N_MLF_X23-67225.ISO" ;;
"ca" | "fr-ca" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_FrenchCanadian_Pro_Ent_EDU_N_MLF_X23-67227.ISO" ;;
"fr" | "fr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_French_Pro_Ent_EDU_N_MLF_X23-67226.ISO" ;;
"he" | "he-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Hebrew_Pro_Ent_EDU_N_MLF_X23-67230.ISO" ;;
"hr" | "hr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Croatian_Pro_Ent_EDU_N_MLF_X23-67218.ISO" ;;
"hu" | "hu-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Hungarian_Pro_Ent_EDU_N_MLF_X23-67231.ISO" ;;
"it" | "it-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Italian_Pro_Ent_EDU_N_MLF_X23-67232.ISO" ;;
"ja" | "ja-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Japanese_Pro_Ent_EDU_N_MLF_X23-67233.ISO" ;;
"ko" | "ko-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Korean_Pro_Ent_EDU_N_MLF_X23-67234.ISO" ;;
"lt" | "lt-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Lithuanian_Pro_Ent_EDU_N_MLF_X23-67236.ISO" ;;
"lv" | "lv-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Latvian_Pro_Ent_EDU_N_MLF_X23-67235.ISO" ;;
"nb" | "nb-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Norwegian_Pro_Ent_EDU_N_MLF_X23-67237.ISO" ;;
"nl" | "nl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Dutch_Pro_Ent_EDU_N_MLF_X23-67221.ISO" ;;
"pl" | "pl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Polish_Pro_Ent_EDU_N_MLF_X23-67238.ISO" ;;
"br" | "pt-br" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Brazilian_Pro_Ent_EDU_N_MLF_X23-67214.ISO" ;;
"pt" | "pt-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Portuguese_Pro_Ent_EDU_N_MLF_X23-67239.ISO" ;;
"ro" | "ro-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Romanian_Pro_Ent_EDU_N_MLF_X23-67240.ISO" ;;
"ru" | "ru-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Russian_Pro_Ent_EDU_N_MLF_X23-67241.ISO" ;;
"sk" | "sk-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Slovak_Pro_Ent_EDU_N_MLF_X23-67243.ISO" ;;
"sl" | "sl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Slovenian_Pro_Ent_EDU_N_MLF_X23-67244.ISO" ;;
"sr" | "sr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Serbian_Latin_Pro_Ent_EDU_N_MLF_X23-67242.ISO" ;;
"sv" | "sv-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Swedish_Pro_Ent_EDU_N_MLF_X23-67247.ISO" ;;
"th" | "th-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Thai_Pro_Ent_EDU_N_MLF_X23-67248.ISO" ;;
"tr" | "tr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Turkish_Pro_Ent_EDU_N_MLF_X23-67249.ISO" ;;
"uk" | "uk-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Ukrainian_Pro_Ent_EDU_N_MLF_X23-67250.ISO" ;;
"zh-hk" | "zh-tw" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_ChnTrad_Pro_Ent_EDU_N_MLF_X23-67217.ISO" ;;
"zh" | "zh-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_ChnSimp_Pro_Ent_EDU_N_MLF_X23-67216.ISO" ;;
esac
;;
"win10arm64-ltsc" | "win10arm64-enterprise-ltsc-eval" )
[[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
size=4430471168
sum="d265df49b30a1477d010c79185a7bc88591a1be4b3eb690c994bed828ea17c00"
url="10/en-us_windows_10_iot_enterprise_ltsc_2021_arm64_dvd_e8d4fc46.iso"
url="en-us_windows_10_iot_enterprise_ltsc_2021_arm64_dvd_e8d4fc46.iso"
;;
esac
@ -624,51 +687,39 @@ getLink1() {
getLink2() {
# Fallbacks for users who cannot connect to the Microsoft servers
local id="$1"
local lang="$2"
local ret="$3"
local url=""
local sum=""
local size=""
local host="https://archive.org/download"
local host="https://dl.bobpony.com/windows"
[[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
case "${id,,}" in
"tiny11" )
size=4480499712
sum="ec6056aa554c17290224af23e1b99961fe99606bb5ea9102d61838939c63325b"
url="tiny11a64/tiny11a64%20r1.iso"
;;
"core11" )
size=3300327424
sum="812dae6b5bf5215db63b61ae10d8f0ffd3aa8529a18d96e9ced53341e2c676ec"
url="tiny11-core-arm64/tiny11%20core%20arm64.iso"
;;
"win11arm64" )
size=5460387840
sum="57d1dfb2c6690a99fe99226540333c6c97d3fd2b557a50dfe3d68c3f675ef2b0"
url="windows-11-24h2-arm64-iso/Win11_24H2_English_Arm64.iso"
;;
"win11arm64-enterprise" | "win11arm64-enterprise-eval" )
size=6872444928
sum="2bf0fd1d5abd267cd0ae8066fea200b3538e60c3e572428c0ec86d4716b61cb7"
url="win11-23h2-en-fr/ARM64/SW_DVD9_Win_Pro_11_23H2_Arm64_English_Pro_Ent_EDU_N_MLF_X23-59519.ISO"
"win11arm64" | "win11arm64-enterprise" | "win11arm64-enterprise-eval" )
size=6326812672
sum="464c75909b9c37864e144886445a2faa67ac86f0845a68cca3f017b97f810e8d"
url="11/en-us_windows_11_23h2_arm64.iso"
;;
"win11arm64-ltsc" | "win11arm64-enterprise-ltsc-eval" )
[[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
size=5121449984
sum="f8f068cdc90c894a55d8c8530db7c193234ba57bb11d33b71383839ac41246b4"
url="Windows11LTSC/X23-81950_26100.1742.240906-0331.ge_release_svc_refresh_CLIENT_ENTERPRISES_OEM_A64FRE_en-us.iso"
url="11/X23-81950_26100.1742.240906-0331.ge_release_svc_refresh_CLIENT_ENTERPRISES_OEM_A64FRE_en-us.iso"
;;
"win10arm64" | "win10arm64-enterprise" | "win10arm64-enterprise-eval" )
size=5192060928
sum="101079b911c8c3dd9c9a88499a16b930fbf00cbaf901761d8265bb3a8fcd9ea9"
url="win-pro-10-22-h-2.15-arm-64-eng-intl-pro-ent-edu-n-mlf-x-23-67222/Win_Pro_10_22H2.15_Arm64_Eng_Intl_Pro_Ent_EDU_N_MLF_X23-67222.ISO"
size=4846794752
sum="6d2688f95fa1d359d68ed0c38c3f38de7b3713c893410e15be9d1e706a4a58c7"
url="10/en-us_windows_10_22h2_arm64.iso"
;;
"win10arm64-ltsc" | "win10arm64-enterprise-ltsc-eval" )
size=4430471168
sum="d265df49b30a1477d010c79185a7bc88591a1be4b3eb690c994bed828ea17c00"
url="windows-10-enterprise-ltsc-full-collection/en-us_windows_10_iot_enterprise_ltsc_2021_arm64_dvd_e8d4fc46.iso"
url="10/en-us_windows_10_iot_enterprise_ltsc_2021_arm64_dvd_e8d4fc46.iso"
;;
esac
@ -793,12 +844,16 @@ addFolder() {
cp -Lr "$folder/." "$dest" || return 1
local file
file=$(find "$dest" -maxdepth 1 -type f -iname install.bat -print -quit)
file=$(find "$dest" -maxdepth 1 -type f -iname install.bat | head -n 1)
[ -f "$file" ] && unix2dos -q "$file"
return 0
}
migrateFiles() {
return 0
}
detectLegacy() {
return 1
}

View file

@ -1,18 +1,16 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${APP:="Windows"}"
: "${MACHINE:="virt"}"
: "${PLATFORM:="arm64"}"
: "${BOOT_MODE:="windows"}"
: "${SUPPORT:="https://github.com/dockur/windows-arm"}"
APP="Windows"
SUPPORT="https://github.com/dockur/windows-arm"
cd /run
. utils.sh # Load functions
. reset.sh # Initialize system
. define.sh # Define versions
. mido.sh # Download Windows
. mido.sh # Download code
. install.sh # Run installation
. disk.sh # Initialize disks
. display.sh # Initialize graphics
@ -39,10 +37,8 @@ fi
terminal
( sleep 30; boot ) &
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
cat "$QEMU_TERM" 2> /dev/null | tee "$QEMU_PTY" | \
sed -u -e 's/\x1B\[[=0-9;]*[a-z]//gi' \
-e 's/failed to load Boot/skipped Boot/g' \
-e 's/0): Not Found/0)/g' & wait $! || :
cat "$QEMU_TERM" 2> /dev/null | tee "$QEMU_PTY" &
wait $! || :
sleep 1 & wait $!
[ ! -f "$QEMU_END" ] && finish 0

View file

@ -10,31 +10,21 @@ EFISYS="efi/microsoft/boot/efisys_noprompt.bin"
skipInstall() {
local iso="$1"
local method=""
local magic byte
local boot="$STORAGE/windows.boot"
local previous="$STORAGE/windows.base"
if [ -f "$previous" ]; then
previous=$(<"$previous")
previous="${previous//[![:print:]]/}"
if [ -n "$previous" ]; then
if [[ "${STORAGE,,}/${previous,,}" != "${iso,,}" ]]; then
previous="$STORAGE/$previous"
if [[ "${previous,,}" != "${iso,,}" ]]; then
if [ -f "$boot" ] && hasDisk; then
if [[ "${iso,,}" == "${STORAGE,,}/windows."* ]]; then
method="your custom .iso file"
else
if [[ "${previous,,}" != "windows."* ]]; then
method="the VERSION variable"
fi
fi
if [ -n "$method" ]; then
info "Detected that $method was changed, but ignoring this because Windows is already installed."
info "Please start with an empty /storage folder, if you want to install a different version of Windows."
fi
info "Detected that the version was changed, but ignoring this because Windows is already installed."
info "Please start with an empty /storage folder, if you want to install a different version of Windows."
return 0
fi
rm -f "$STORAGE/$previous"
[ -f "$previous" ] && rm -f "$previous"
return 1
fi
fi
@ -86,6 +76,8 @@ startInstall() {
BOOT="$STORAGE/$file"
! migrateFiles "$BOOT" "$VERSION" && error "Migration failed!" && exit 57
fi
skipInstall "$BOOT" && return 1
@ -128,8 +120,6 @@ finishInstall() {
rm -f "$STORAGE/windows.old"
rm -f "$STORAGE/windows.vga"
rm -f "$STORAGE/windows.net"
rm -f "$STORAGE/windows.usb"
rm -f "$STORAGE/windows.args"
rm -f "$STORAGE/windows.base"
rm -f "$STORAGE/windows.boot"
@ -172,22 +162,10 @@ finishInstall() {
echo "$ARGS" > "$STORAGE/windows.args"
fi
if [ -n "${VGA:-}" ] && [[ "${VGA:-}" != "virtio"* ]]; then
echo "$VGA" > "$STORAGE/windows.vga"
fi
if [ -n "${USB:-}" ] && [[ "${USB:-}" != "qemu-xhci"* ]]; then
echo "$USB" > "$STORAGE/windows.usb"
fi
if [ -n "${DISK_TYPE:-}" ] && [[ "${DISK_TYPE:-}" != "scsi" ]]; then
echo "$DISK_TYPE" > "$STORAGE/windows.type"
fi
if [ -n "${ADAPTER:-}" ] && [[ "${ADAPTER:-}" != "virtio-net-pci" ]]; then
echo "$ADAPTER" > "$STORAGE/windows.net"
fi
rm -rf "$TMP"
return 0
}
@ -199,9 +177,8 @@ abortInstall() {
local efi
[[ "${iso,,}" == *".esd" ]] && exit 60
[[ "${UNPACK:-}" == [Yy1]* ]] && exit 60
efi=$(find "$dir" -maxdepth 1 -type d -iname efi -print -quit)
efi=$(find "$dir" -maxdepth 1 -type d -iname efi | head -n 1)
if [ -z "$efi" ]; then
[[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
@ -222,23 +199,13 @@ abortInstall() {
return 1
}
findFile() {
detectCustom() {
local dir file base
local fname="$1"
local boot="$STORAGE/windows.boot"
local file base
CUSTOM=""
dir=$(find / -maxdepth 1 -type d -iname "$fname" -print -quit)
[ ! -d "$dir" ] && dir=$(find "$STORAGE" -maxdepth 1 -type d -iname "$fname" -print -quit)
if [ -d "$dir" ]; then
if ! hasDisk || [ ! -f "$boot" ]; then
error "The bind $dir maps to a file that does not exist!" && return 1
fi
fi
file=$(find / -maxdepth 1 -type f -iname "$fname" -print -quit)
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname "$fname" -print -quit)
file=$(find / -maxdepth 1 -type f -iname custom.iso | head -n 1)
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname custom.iso | head -n 1)
if [ ! -s "$file" ] && [[ "${VERSION,,}" != "http"* ]]; then
base=$(basename "$VERSION")
@ -254,25 +221,12 @@ findFile() {
[ -z "$size" ] || [[ "$size" == "0" ]] && return 0
ISO="$file"
CUSTOM="$file"
CUSTOM="$ISO"
BOOT="$STORAGE/windows.$size.iso"
return 0
}
detectCustom() {
CUSTOM=""
! findFile "custom.iso" && return 1
[ -n "$CUSTOM" ] && return 0
! findFile "boot.iso" && return 1
[ -n "$CUSTOM" ] && return 0
return 0
}
extractESD() {
local iso="$1"
@ -292,12 +246,12 @@ extractESD() {
mkdir -p "$dir"
size=16106127360
size_gb=$(formatBytes "$size")
size_gb=$(( (size + 1073741823)/1073741824 ))
space=$(df --output=avail -B 1 "$dir" | tail -n 1)
space_gb=$(formatBytes "$space")
space_gb=$(( (space + 1073741823)/1073741824 ))
if (( size > space )); then
error "Not enough free space in $STORAGE, have $space_gb available but need at least $size_gb." && return 1
error "Not enough free space in $STORAGE, have $space_gb GB available but need at least $size_gb GB." && return 1
fi
local esdImageCount
@ -362,7 +316,7 @@ extractImage() {
local dir="$2"
local version="$3"
local desc="local ISO"
local file size size_gb space space_gb
local size size_gb space space_gb
if [ -z "$CUSTOM" ]; then
desc="downloaded ISO"
@ -383,16 +337,16 @@ extractImage() {
mkdir -p "$dir"
size=$(stat -c%s "$iso")
size_gb=$(formatBytes "$size")
size_gb=$(( (size + 1073741823)/1073741824 ))
space=$(df --output=avail -B 1 "$dir" | tail -n 1)
space_gb=$(formatBytes "$space")
space_gb=$(( (space + 1073741823)/1073741824 ))
if ((size<100000000)); then
error "Invalid ISO file: Size is smaller than 100 MB" && return 1
fi
if (( size > space )); then
error "Not enough free space in $STORAGE, have $space_gb available but need at least $size_gb." && return 1
error "Not enough free space in $STORAGE, have $space_gb GB available but need at least $size_gb GB." && return 1
fi
rm -rf "$dir"
@ -401,26 +355,7 @@ extractImage() {
error "Failed to extract ISO file: $iso" && return 1
fi
if [[ "${UNPACK:-}" != [Yy1]* ]]; then
LABEL=$(isoinfo -d -i "$iso" | sed -n 's/Volume id: //p')
else
file=$(find "$dir" -maxdepth 1 -type f -iname "*.iso" -print -quit)
if [ -z "$file" ]; then
error "Failed to find any .iso file in archive!" && return 1
fi
if ! 7z x "$file" -o"$dir" > /dev/null; then
error "Failed to extract archive!" && return 1
fi
LABEL=$(isoinfo -d -i "$file" | sed -n 's/Volume id: //p')
rm -f "$file"
fi
LABEL=$(isoinfo -d -i "$iso" | sed -n 's/Volume id: //p')
return 0
}
@ -554,10 +489,6 @@ setXML() {
local file="/custom.xml"
if [ -d "$file" ]; then
error "The bind $file maps to a file that does not exist!" && exit 67
fi
[ ! -f "$file" ] || [ ! -s "$file" ] && file="$STORAGE/custom.xml"
[ ! -f "$file" ] || [ ! -s "$file" ] && file="/run/assets/custom.xml"
[ ! -f "$file" ] || [ ! -s "$file" ] && file="$1"
@ -602,14 +533,14 @@ detectImage() {
fi
local src wim info
src=$(find "$dir" -maxdepth 1 -type d -iname sources -print -quit)
src=$(find "$dir" -maxdepth 1 -type d -iname sources | head -n 1)
if [ ! -d "$src" ]; then
warn "failed to locate 'sources' folder in ISO image, $FB" && return 1
fi
wim=$(find "$src" -maxdepth 1 -type f -iname install.wim -print -quit)
[ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname install.esd -print -quit)
wim=$(find "$src" -maxdepth 1 -type f -iname install.wim | head -n 1)
[ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname install.esd | head -n 1)
if [ ! -f "$wim" ]; then
warn "failed to locate 'install.wim' or 'install.esd' in ISO image, $FB" && return 1
@ -642,10 +573,6 @@ detectImage() {
info "Detected: $desc"
setXML "" && return 0
if [[ "$DETECTED" == "win81x86"* ]] || [[ "$DETECTED" == "win10x86"* ]]; then
error "The 32-bit version of $desc is not supported!" && return 1
fi
msg="the answer file for $desc was not found ($DETECTED.xml)"
local fallback="/run/assets/${DETECTED%%-*}.xml"
@ -693,10 +620,6 @@ updateXML() {
local language="$2"
local culture region user admin pass keyboard
if [ -n "${VM_NET_IP:-}" ]; then
sed -i "s/ 20.20.20.1 / ${VM_NET_IP%.*}.1 /g" "$asset"
fi
[ -z "$HEIGHT" ] && HEIGHT="720"
[ -z "$WIDTH" ] && WIDTH="1280"
@ -734,26 +657,15 @@ updateXML() {
sed -i "s/<Username>Docker<\/Username>/<Username>$user<\/Username>/g" "$asset"
fi
[ -n "$PASSWORD" ] && pass="$PASSWORD" || pass="admin"
pw=$(printf '%s' "${pass}Password" | iconv -f utf-8 -t utf-16le | base64 -w 0)
admin=$(printf '%s' "${pass}AdministratorPassword" | iconv -f utf-8 -t utf-16le | base64 -w 0)
sed -i "s/<Value>password<\/Value>/<Value>$admin<\/Value>/g" "$asset"
sed -i "s/<PlainText>true<\/PlainText>/<PlainText>false<\/PlainText>/g" "$asset"
sed -z "s/<Password>...........<Value \/>/<Password>\n <Value>$pw<\/Value>/g" -i "$asset"
sed -z "s/<Password>...............<Value \/>/<Password>\n <Value>$pw<\/Value>/g" -i "$asset"
sed -z "s/<AdministratorPassword>...........<Value \/>/<AdministratorPassword>\n <Value>$admin<\/Value>/g" -i "$asset"
sed -z "s/<AdministratorPassword>...............<Value \/>/<AdministratorPassword>\n <Value>$admin<\/Value>/g" -i "$asset"
if [ -n "$EDITION" ]; then
[[ "${EDITION^^}" == "CORE" ]] && EDITION="STANDARDCORE"
sed -i "s/SERVERSTANDARD<\/Value>/SERVER${EDITION^^}<\/Value>/g" "$asset"
fi
if [ -n "$KEY" ]; then
sed -i '/<ProductKey>/,/<\/ProductKey>/d' "$asset"
sed -i "s/<\/UserData>/ <ProductKey>\n <Key>${KEY}<\/Key>\n <WillShowUI>OnError<\/WillShowUI>\n <\/ProductKey>\n <\/UserData>/g" "$asset"
if [ -n "$PASSWORD" ]; then
pass=$(printf '%s' "${PASSWORD}Password" | iconv -f utf-8 -t utf-16le | base64 -w 0)
admin=$(printf '%s' "${PASSWORD}AdministratorPassword" | iconv -f utf-8 -t utf-16le | base64 -w 0)
sed -i "s/<Value>password<\/Value>/<Value>$admin<\/Value>/g" "$asset"
sed -i "s/<PlainText>true<\/PlainText>/<PlainText>false<\/PlainText>/g" "$asset"
sed -z "s/<Password>...........<Value \/>/<Password>\n <Value>$pass<\/Value>/g" -i "$asset"
sed -z "s/<Password>...............<Value \/>/<Password>\n <Value>$pass<\/Value>/g" -i "$asset"
sed -z "s/<AdministratorPassword>...........<Value \/>/<AdministratorPassword>\n <Value>$admin<\/Value>/g" -i "$asset"
sed -z "s/<AdministratorPassword>...............<Value \/>/<AdministratorPassword>\n <Value>$admin<\/Value>/g" -i "$asset"
fi
return 0
@ -765,13 +677,8 @@ addDriver() {
local path="$2"
local target="$3"
local driver="$4"
local desc=""
local folder=""
if [ -z "$id" ]; then
warn "no Windows version specified for \"$driver\" driver!" && return 0
fi
case "${id,,}" in
"win7x86"* ) folder="w7/x86" ;;
"win7x64"* ) folder="w7/amd64" ;;
@ -791,12 +698,7 @@ addDriver() {
esac
if [ -z "$folder" ]; then
desc=$(printVersion "$id" "$id")
if [[ "${id,,}" != *"x86"* ]]; then
warn "no \"$driver\" driver available for \"$desc\" !" && return 0
else
warn "no \"$driver\" driver available for the 32-bit version of \"$desc\" !" && return 0
fi
warn "no \"$driver\" driver found for \"$DETECTED\" !" && return 0
fi
[ ! -d "$path/$driver/$folder" ] && return 0
@ -829,12 +731,7 @@ addDrivers() {
local msg="Adding drivers to image..."
info "$msg" && html "$msg"
if [ -z "$version" ]; then
version="win11x64"
warn "Windows version unknown, falling back to Windows 11 drivers..."
fi
if ! bsdtar -xf /var/drivers.txz -C "$drivers"; then
if ! bsdtar -xf /drivers.txz -C "$drivers"; then
error "Failed to extract drivers from archive!" && return 1
fi
@ -847,7 +744,7 @@ addDrivers() {
addDriver "$version" "$drivers" "$target" "qxl" || return 1
addDriver "$version" "$drivers" "$target" "viofs" || return 1
addDriver "$version" "$drivers" "$target" "sriov" || return 1
addDriver "$version" "$drivers" "$target" "smbus" || return 1
# Disable temporarily : addDriver "$version" "$drivers" "$target" "smbus" || return 1
addDriver "$version" "$drivers" "$target" "qxldod" || return 1
addDriver "$version" "$drivers" "$target" "viorng" || return 1
addDriver "$version" "$drivers" "$target" "viostor" || return 1
@ -855,7 +752,7 @@ addDrivers() {
addDriver "$version" "$drivers" "$target" "NetKVM" || return 1
addDriver "$version" "$drivers" "$target" "Balloon" || return 1
addDriver "$version" "$drivers" "$target" "vioscsi" || return 1
addDriver "$version" "$drivers" "$target" "pvpanic" || return 1
# Disable temporarily : addDriver "$version" "$drivers" "$target" "pvpanic" || return 1
addDriver "$version" "$drivers" "$target" "vioinput" || return 1
addDriver "$version" "$drivers" "$target" "viogpudo" || return 1
addDriver "$version" "$drivers" "$target" "vioserial" || return 1
@ -903,14 +800,14 @@ updateImage() {
rm -rf "$tmp"
mkdir -p "$tmp"
src=$(find "$dir" -maxdepth 1 -type d -iname sources -print -quit)
src=$(find "$dir" -maxdepth 1 -type d -iname sources | head -n 1)
if [ ! -d "$src" ]; then
error "failed to locate 'sources' folder in ISO image, $FB" && return 1
fi
wim=$(find "$src" -maxdepth 1 -type f -iname boot.wim -print -quit)
[ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname boot.esd -print -quit)
wim=$(find "$src" -maxdepth 1 -type f -iname boot.wim | head -n 1)
[ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname boot.esd | head -n 1)
if [ ! -f "$wim" ]; then
error "failed to locate 'boot.wim' or 'boot.esd' in ISO image, $FB" && return 1
@ -973,7 +870,7 @@ updateImage() {
local find="$file"
[[ "$MANUAL" == [Yy1]* ]] && find="$org"
path=$(find "$dir" -maxdepth 1 -type f -iname "$find" -print -quit)
path=$(find "$dir" -maxdepth 1 -type f -iname "$find" | head -n 1)
if [ -f "$path" ]; then
if [[ "$MANUAL" != [Yy1]* ]]; then
@ -1027,12 +924,12 @@ buildImage() {
fi
size=$(du -h -b --max-depth=0 "$dir" | cut -f1)
size_gb=$(formatBytes "$size")
size_gb=$(( (size + 1073741823)/1073741824 ))
space=$(df --output=avail -B 1 "$TMP" | tail -n 1)
space_gb=$(formatBytes "$space")
space_gb=$(( (space + 1073741823)/1073741824 ))
if (( size > space )); then
error "Not enough free space in $STORAGE, have $space_gb available but need at least $size_gb." && return 1
error "Not enough free space in $STORAGE, have $space_gb GB available but need at least $size_gb GB." && return 1
fi
if [[ "${BOOT_MODE,,}" != "windows_legacy" ]]; then
@ -1076,47 +973,52 @@ bootWindows() {
if [ -f "$STORAGE/windows.args" ]; then
ARGS=$(<"$STORAGE/windows.args")
ARGS="${ARGS//[![:print:]]/}"
ARGUMENTS="$ARGS ${ARGUMENTS:-}"
fi
if [ -s "$STORAGE/windows.vga" ] && [ -f "$STORAGE/windows.vga" ]; then
if [ -z "${VGA:-}" ]; then
VGA=$(<"$STORAGE/windows.vga")
VGA="${VGA//[![:print:]]/}"
fi
fi
if [ -s "$STORAGE/windows.usb" ] && [ -f "$STORAGE/windows.usb" ]; then
if [ -z "${USB:-}" ]; then
USB=$(<"$STORAGE/windows.usb")
USB="${USB//[![:print:]]/}"
fi
fi
if [ -s "$STORAGE/windows.net" ] && [ -f "$STORAGE/windows.net" ]; then
if [ -z "${ADAPTER:-}" ]; then
ADAPTER=$(<"$STORAGE/windows.net")
ADAPTER="${ADAPTER//[![:print:]]/}"
fi
fi
if [ -s "$STORAGE/windows.type" ] && [ -f "$STORAGE/windows.type" ]; then
if [ -z "${DISK_TYPE:-}" ]; then
DISK_TYPE=$(<"$STORAGE/windows.type")
DISK_TYPE="${DISK_TYPE//[![:print:]]/}"
fi
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE=$(<"$STORAGE/windows.type")
fi
if [ -s "$STORAGE/windows.mode" ] && [ -f "$STORAGE/windows.mode" ]; then
BOOT_MODE=$(<"$STORAGE/windows.mode")
BOOT_MODE="${BOOT_MODE//[![:print:]]/}"
if [ -s "$STORAGE/windows.old" ] && [ -f "$STORAGE/windows.old" ]; then
[[ "${PLATFORM,,}" == "x64" ]] && MACHINE=$(<"$STORAGE/windows.old")
fi
return 0
fi
if [ -s "$STORAGE/windows.old" ] && [ -f "$STORAGE/windows.old" ]; then
if [[ "${PLATFORM,,}" == "x64" ]]; then
MACHINE=$(<"$STORAGE/windows.old")
MACHINE="${MACHINE//[![:print:]]/}"
# Migrations
[[ "${PLATFORM,,}" != "x64" ]] && return 0
if [ -f "$STORAGE/windows.old" ]; then
MACHINE=$(<"$STORAGE/windows.old")
[ -z "$MACHINE" ] && MACHINE="q35"
BOOT_MODE="windows_legacy"
echo "$BOOT_MODE" > "$STORAGE/windows.mode"
return 0
fi
local creation="1.10"
local minimal="2.14"
if [ -f "$STORAGE/windows.ver" ]; then
creation=$(<"$STORAGE/windows.ver")
[[ "${creation}" != *"."* ]] && creation="$minimal"
fi
# Force secure boot on installs created prior to v2.14
if (( $(echo "$creation < $minimal" | bc -l) )); then
if [[ "${BOOT_MODE,,}" == "windows" ]]; then
BOOT_MODE="windows_secure"
echo "$BOOT_MODE" > "$STORAGE/windows.mode"
if [ -f "$STORAGE/windows.rom" ] && [ ! -f "$STORAGE/$BOOT_MODE.rom" ]; then
mv -f "$STORAGE/windows.rom" "$STORAGE/$BOOT_MODE.rom"
fi
if [ -f "$STORAGE/windows.vars" ] && [ ! -f "$STORAGE/$BOOT_MODE.vars" ]; then
mv -f "$STORAGE/windows.vars" "$STORAGE/$BOOT_MODE.vars"
fi
fi
fi

View file

@ -4,26 +4,25 @@ set -Eeuo pipefail
handle_curl_error() {
local error_code="$1"
local server_name="$2"
case "$error_code" in
1) error "Unsupported protocol!" ;;
2) error "Failed to initialize curl!" ;;
3) error "The URL format is malformed!" ;;
5) error "Failed to resolve address of proxy host!" ;;
6) error "Failed to resolve $server_name servers! Is there an Internet connection?" ;;
7) error "Failed to contact $server_name servers! Is there an Internet connection or is the server down?" ;;
8) error "$server_name servers returned a malformed HTTP response!" ;;
6) error "Failed to resolve Microsoft servers! Is there an Internet connection?" ;;
7) error "Failed to contact Microsoft servers! Is there an Internet connection or is the server down?" ;;
8) error "Microsoft servers returned a malformed HTTP response!" ;;
16) error "A problem was detected in the HTTP2 framing layer!" ;;
22) error "$server_name servers returned a failing HTTP status code!" ;;
22) error "Microsoft servers returned a failing HTTP status code!" ;;
23) error "Failed at writing Windows media to disk! Out of disk space or permission error?" ;;
26) error "Failed to read Windows media from disk!" ;;
27) error "Ran out of memory during download!" ;;
28) error "Connection timed out to $server_name server!" ;;
35) error "SSL connection error from $server_name server!" ;;
28) error "Connection timed out to Microsoft server!" ;;
35) error "SSL connection error from Microsoft server!" ;;
36) error "Failed to continue earlier download!" ;;
52) error "Received no data from the $server_name server!" ;;
63) error "$server_name servers returned an unexpectedly large response!" ;;
52) error "Received no data from the Microsoft server!" ;;
63) error "Microsoft servers returned an unexpectedly large response!" ;;
# POSIX defines exit statuses 1-125 as usable by us
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
$((error_code <= 125)))
@ -93,7 +92,6 @@ download_windows() {
# uuidgen: For MacOS (installed by default) and other systems (e.g. with no /proc) that don't have a kernel interface for generating random UUIDs
session_id=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random)
session_id="${session_id//[![:print:]]/}"
# Get product edition ID for latest release of given Windows version
# Product edition ID: This specifies both the Windows release (e.g. 22H2) and edition ("multi-edition" is default, either Home/Pro/Edu/etc., we select "Pro" in the answer files) in one number
@ -102,7 +100,7 @@ download_windows() {
# Remove "Accept" header that curl sends by default
[[ "$DEBUG" == [Yy1]* ]] && echo "Parsing download page: ${url}"
download_page_html=$(curl --silent --max-time 30 --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url") || {
handle_curl_error "$?" "Microsoft"
handle_curl_error $?
return $?
}
@ -119,14 +117,14 @@ download_windows() {
# Permit Session ID
curl --silent --max-time 30 --output /dev/null --user-agent "$user_agent" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://vlscppe.microsoft.com/tags?org_id=y6jn8c31&session_id=$session_id" || {
# This should only happen if there's been some change to how this API works
handle_curl_error "$?" "Microsoft"
handle_curl_error $?
return $?
}
[[ "$DEBUG" == [Yy1]* ]] && echo -n "Getting language SKU ID: "
sku_url="https://www.microsoft.com/software-download-connector/api/getskuinformationbyproductedition?profile=$profile&ProductEditionId=$product_edition_id&SKU=undefined&friendlyFileName=undefined&Locale=en-US&sessionID=$session_id"
language_skuid_json=$(curl --silent --max-time 30 --request GET --user-agent "$user_agent" --referer "$url" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "$sku_url") || {
handle_curl_error "$?" "Microsoft"
handle_curl_error $?
return $?
}
@ -209,9 +207,6 @@ download_windows_eval() {
"win2022-eval" )
enterprise_type="server"
windows_version="windows-server-2022" ;;
"win2019-hv" )
enterprise_type="server"
windows_version="hyper-v-server-2019" ;;
"win2019-eval" )
enterprise_type="server"
windows_version="windows-server-2019" ;;
@ -234,7 +229,7 @@ download_windows_eval() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Parsing download page: ${url}"
iso_download_page_html=$(curl --silent --max-time 30 --user-agent "$user_agent" --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url") || {
handle_curl_error "$?" "Microsoft"
handle_curl_error $?
return $?
}
@ -246,10 +241,10 @@ download_windows_eval() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Getting download link.."
filter="https://go.microsoft.com/fwlink/?linkid=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country,,}"
if ! echo "$iso_download_page_html" | grep -io "$filter" > /dev/null; then
filter="https://go.microsoft.com/fwlink/p/?linkid=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country,,}"
if [[ "$enterprise_type" == "iot" ]]; then
filter="https://go.microsoft.com/fwlink/?linkid=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}"
else
filter="https://go.microsoft.com/fwlink/p/?LinkID=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}"
fi
iso_download_links=$(echo "$iso_download_page_html" | grep -io "$filter") || {
@ -292,7 +287,7 @@ download_windows_eval() {
iso_download_link=$(curl --silent --max-time 30 --user-agent "$user_agent" --location --output /dev/null --silent --write-out "%{url_effective}" --head --fail --proto =https --tlsv1.2 --http1.1 -- "$iso_download_link") || {
# This should only happen if the Microsoft servers are down
handle_curl_error "$?" "Microsoft"
handle_curl_error $?
return $?
}
@ -314,7 +309,7 @@ getWindows() {
info "$msg" && html "$msg"
case "${version,,}" in
"win2008r2" | "win81${PLATFORM,,}"* | "win11${PLATFORM,,}-enterprise-iot"* | "win11${PLATFORM,,}-enterprise-ltsc"* )
"win2008r2" | "win81${PLATFORM,,}-enterprise"* | "win11${PLATFORM,,}-enterprise-iot"* | "win11${PLATFORM,,}-enterprise-ltsc"* )
if [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-"* ]]; then
error "No download in the $language language available for $edition!"
MIDO_URL="" && return 1
@ -339,7 +334,7 @@ getWindows() {
"win11${PLATFORM,,}-enterprise"* | "win10${PLATFORM,,}-enterprise"* )
download_windows_eval "$version" "$lang" "$edition" && return 0
;;
"win2025-eval" | "win2022-eval" | "win2019-eval" | "win2019-hv" | "win2016-eval" | "win2012r2-eval" )
"win2025-eval" | "win2022-eval" | "win2019-eval" | "win2016-eval" | "win2012r2-eval" )
download_windows_eval "$version" "$lang" "$edition" && return 0
;;
"win81${PLATFORM,,}-enterprise"* | "win2008r2" )
@ -347,10 +342,53 @@ getWindows() {
* ) error "Invalid VERSION specified, value \"$version\" is not recognized!" ;;
esac
MIDO_URL=$(getMido "$version" "$lang" "")
[ -z "$MIDO_URL" ] && return 1
if [[ "${PLATFORM,,}" != "x64" ]]; then
MIDO_URL=""
return 1
fi
return 0
if [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-"* ]]; then
MIDO_URL=""
return 1
fi
case "${version,,}" in
"win81${PLATFORM,,}-enterprise"* )
MIDO_URL="https://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X64FREE_EN-US_DV9.ISO"
return 0
;;
"win11${PLATFORM,,}-enterprise-iot"* | "win11${PLATFORM,,}-enterprise-ltsc"* )
MIDO_URL="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66749/26100.1.240331-1435.ge_release_CLIENT_IOT_LTSC_EVAL_x64FRE_en-us.iso"
return 0
;;
"win2025-eval" )
MIDO_URL="https://software-static.download.prss.microsoft.com/dbazure/888969d5-f34g-4e03-ac9d-1f9786c66749/26100.1.240331-1435.ge_release_SERVER_EVAL_x64FRE_en-us.iso"
return 0
;;
"win2022-eval" )
MIDO_URL="https://software-static.download.prss.microsoft.com/sg/download/888969d5-f34g-4e03-ac9d-1f9786c66749/SERVER_EVAL_x64FRE_en-us.iso"
return 0
;;
"win2019-eval" )
MIDO_URL="https://software-download.microsoft.com/download/pr/17763.737.190906-2324.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us_1.iso"
return 0
;;
"win2016-eval" )
MIDO_URL="https://software-download.microsoft.com/download/pr/Windows_Server_2016_Datacenter_EVAL_en-us_14393_refresh.ISO"
return 0
;;
"win2012r2-eval" )
MIDO_URL="https://download.microsoft.com/download/6/2/A/62A76ABB-9990-4EFC-A4FE-C7D698DAEB96/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_SERVER_EVAL_EN-US-IR3_SSS_X64FREE_EN-US_DV9.ISO"
return 0
;;
"win2008r2" )
MIDO_URL="https://download.microsoft.com/download/4/1/D/41DEA7E0-B30D-4012-A1E3-F24DC03BA1BB/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso"
return 0
;;
esac
MIDO_URL=""
return 1
}
getCatalog() {
@ -472,18 +510,6 @@ getESD() {
return 0
}
isCompressed() {
local file="$1"
case "${file,,}" in
*".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
return 0 ;;
esac
return 1
}
verifyFile() {
local iso="$1"
@ -492,9 +518,7 @@ verifyFile() {
local check="$4"
if [ -n "$size" ] && [[ "$total" != "$size" ]] && [[ "$size" != "0" ]]; then
if [[ "$VERIFY" == [Yy1]* ]] || [[ "$DEBUG" == [Yy1]* ]]; then
warn "The downloaded file has a different size ( $total bytes) than expected ( $size bytes). Please report this at $SUPPORT/issues"
fi
warn "The downloaded file has an unexpected size: $total bytes, while expected value was: $size bytes. Please report this at $SUPPORT/issues"
fi
local hash=""
@ -517,7 +541,7 @@ verifyFile() {
info "Succesfully verified ISO!" && return 0
fi
error "The downloaded file has an unknown $algo checksum: $hash , as the expected value was: $check. Please report this at $SUPPORT/issues"
error "The downloaded file has an invalid $algo checksum: $hash , while expected value was: $check. Please report this at $SUPPORT/issues"
return 1
}
@ -529,17 +553,14 @@ downloadFile() {
local size="$4"
local lang="$5"
local desc="$6"
local msg="Downloading $desc"
local rc total total_gb progress domain dots agent space folder
local rc total progress domain dots space folder
rm -f "$iso"
agent=$(get_agent)
if [ -n "$size" ] && [[ "$size" != "0" ]]; then
folder=$(dirname -- "$iso")
space=$(df --output=avail -B 1 "$folder" | tail -n 1)
total_gb=$(formatBytes "$space")
(( size > space )) && error "Not enough free space to download file, only $total_gb left!" && return 1
(( size > space )) && error "Not enough free space left to download file!" && return 1
fi
# Check if running with interactive TTY or redirected to docker log
@ -549,8 +570,8 @@ downloadFile() {
progress="--progress=dot:giga"
fi
local msg="Downloading $desc"
html "$msg..."
/run/progress.sh "$iso" "$size" "$msg ([P])..." &
domain=$(echo "$url" | awk -F/ '{print $3}')
dots=$(echo "$domain" | tr -cd '.' | wc -c)
@ -561,26 +582,25 @@ downloadFile() {
fi
info "$msg..."
/run/progress.sh "$iso" "$size" "$msg ([P])..." &
{ wget "$url" -O "$iso" -q --timeout=30 --no-http-keep-alive --user-agent "$agent" --show-progress "$progress"; rc=$?; } || :
{ wget "$url" -O "$iso" -q --timeout=30 --no-http-keep-alive --show-progress "$progress"; rc=$?; } || :
fKill "progress.sh"
if (( rc == 0 )) && [ -f "$iso" ]; then
total=$(stat -c%s "$iso")
total_gb=$(formatBytes "$total")
if [ "$total" -lt 100000000 ]; then
error "Invalid download link: $url (is only $total_gb ?). Please report this at $SUPPORT/issues" && return 1
error "Invalid download link: $url (is only $total bytes?). Please report this at $SUPPORT/issues." && return 1
fi
verifyFile "$iso" "$size" "$total" "$sum" || return 1
isCompressed "$url" && UNPACK="Y"
html "Download finished successfully..." && return 0
fi
msg="Failed to download $url"
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && return 1
(( rc == 4 )) && error "$msg , network failure!" && return 1
(( rc == 8 )) && error "$msg , server issued an error response! Please report this at $SUPPORT/issues" && return 1
(( rc == 8 )) && error "$msg , server issued an error response!" && return 1
error "$msg , reason: $rc"
return 1
@ -598,14 +618,12 @@ downloadImage() {
local msg="Will retry after $delay seconds..."
if [[ "${version,,}" == "http"* ]]; then
base=$(basename "$iso")
desc=$(fromFile "$base")
downloadFile "$iso" "$version" "" "" "" "$desc" && return 0
info "$msg" && html "$msg" && sleep "$delay"
downloadFile "$iso" "$version" "" "" "" "$desc" && return 0
rm -f "$iso"
return 1
fi

View file

@ -29,7 +29,7 @@ boot() {
if [ -s "$QEMU_PTY" ]; then
if [ "$(stat -c%s "$QEMU_PTY")" -gt 7 ]; then
info "Windows started succesfully, visit http://127.0.0.1:8006/ to view the screen..."
info "Windows started succesfully, visit http://localhost:8006/ to view the screen..."
return 0
fi
fi

View file

@ -14,21 +14,16 @@ if [[ "$DHCP" == [Yy1]* ]]; then
interface="$VM_NET_DEV"
fi
if [[ "${NETWORK,,}" == "user"* ]]; then
interface="127.0.0.1"
fi
addShare() {
local dir="$1"
local name="$2"
local comment="$3"
mkdir -p "$dir" || return 1
ls -A "$dir" >/dev/null 2>&1 || return 1
if [ -z "$(ls -A "$dir")" ]; then
chmod 777 "$dir" || return 1
chmod 777 "$dir"
{ echo "--------------------------------------------------------"
echo " $APP for Docker v$(</run/version)..."
@ -76,9 +71,6 @@ addShare() {
echo " guest account = nobody"
echo " map to guest = Bad User"
echo " server min protocol = NT1"
echo " follow symlinks = yes"
echo " wide links = yes"
echo " unix extensions = no"
echo ""
echo " # disable printing services"
echo " load printers = no"
@ -92,35 +84,17 @@ share="/data"
[ ! -d "$share" ] && [ -d "/shared" ] && share="/shared"
[ ! -d "$share" ] && [ -d "$STORAGE/shared" ] && share="$STORAGE/shared"
if ! addShare "$share" "Data" "Shared"; then
error "Failed to add shared folder '$share'. Please check its permissions." && return 0
fi
addShare "$share" "Data" "Shared" || error "Failed to create shared folder!"
if [ -d "/data2" ]; then
addShare "/data2" "Data2" "Shared" || error "Failed to add shared folder '/data2'. Please check its permissions."
fi
if [ -d "/data3" ]; then
addShare "/data3" "Data3" "Shared" || error "Failed to add shared folder '/data3'. Please check its permissions."
fi
IFS=',' read -r -a dirs <<< "${SHARES:-}"
for dir in "${dirs[@]}"; do
[ ! -d "$dir" ] && continue
dir_name=$(basename "$dir")
addShare "$dir" "$dir_name" "Shared $dir_name" || error "Failed to create shared folder for $dir!"
done
# Fix Samba permissions
[ -d /run/samba/msg.lock ] && chmod -R 0755 /run/samba/msg.lock
[ -d /var/log/samba/cores ] && chmod -R 0700 /var/log/samba/cores
[ -d /var/cache/samba/msg.lock ] && chmod -R 0755 /var/cache/samba/msg.lock
[ -d "/data2" ] && addShare "/data2" "Data2" "Shared"
[ -d "/data3" ] && addShare "/data3" "Data3" "Shared"
if ! smbd; then
error "Samba daemon failed to start!"
smbd -i --debug-stdout || true
fi
# Enable Web Service Discovery
wsdd -i "$interface" -p -n "$hostname" &
echo "$!" > /var/run/wsdd.pid