utmapp / UTM

Virtual machines for iOS and macOS
https://getutm.app
Apache License 2.0
26.58k stars 1.33k forks source link

Resize disk0.img with macOS VM #4186

Open marek90 opened 2 years ago

marek90 commented 2 years ago

Is there a way how to resize an existing disk (the primary one) of an macOS VM? It defaults to 64GB and thanks to APFS it occupies only the real size of data within (via du -sh on the disk image) even though Finder reports 64GB (via Get Info on the disk image).

This all works well with APFS "magic" 🪄 but if I want to backup the VM to an external non-APFS it will occupy all of the 64GB.

So the workaround I tried is to install macOS Monterey with the minimal disk size (24GB), but now I have only 2GB of free space left. Is there a way how I can resize the disk of an existing VM? I understand I could always create a new disk and attach it to the VM, but what about resizing the (primary) disk of the VM? Is that at all possible with macOS?

conath commented 2 years ago

This isn't possible in UTM. I found this article that explains how to resize a disk image on Mac - could you please try and see if it works for your case? Make sure you have a backup of the file in case it gets damaged in your experimentation!

natevw commented 2 years ago

I experimented with this some today — I was hoping to grow a macOS (M1) guest big enough that I could install system updates within it, without having to set up everything inside again. I was able to make some progress but at this point didn't succeed.

Based on the tips in #2636 I started with qemu-img resize. Specifically what did work was:

qemu-img resize -f raw /Users/natevw/Library/Containers/com.utmapp.UTM/Data/Documents/My\ macOS\ VM.utm/Data/disk0.img +30G

The -f raw part seems to be key — without it the raw image file itself grows but the macOS guest does not see any change.

At this point the VM still boots just fine and I do have a larger "Virtual Block Media" device ("Show All Devices" in guest Disk Utility app) so it's off to a good start!

But unfortunately that's about as far as I can get, afaict, due to the partition arrangement:

Screenshot of terminal running `diskutil list`, `diskutil resize`, and `diskutil eraseVolume` commands with respective output, plus a Disk Utility window in background showing 66GB storage device

It looks the original arrangement of the disk is:

  1. A small Apple_APFS_ISC "iBoot System Container" boot partition
  2. The main Apple_APFS container ("Macintosh HD" and various other volumes within)
  3. A separate Apple_APFS_Recovery at the [original] end of the block media

I don't think I can resize the partition for the main APFS container, without first moving or removing the recovery partition. See https://apple.stackexchange.com/questions/364288/how-to-resize-grow-apfs-partition-that-is-at-the-end-of-the-drive which provided examples for some of the commands I tried.

Since my host is also Monterrey it looks like I won't be able to boot into recovery mode (see #3526) so I haven't tried anything from there. I suppose it might be possible to mount this whole disk0.img image as a secondary disk in another VM and try fix it up there, but I haven't pursued that route yet anyway.


UPDATE: I won't likely pursue this any further in the near future. By temporarily setting both the old and a clean-install new VM to "bridge" networking mode, I was able to use Migration Assistant to transfer over earlier work/settings from my original guest, into a new guest with a larger disk.

(I also tried using a second disk image as a Time Machine backup volume, also aiming to use Migration Assistant but only on the new VM. That attempt also failed — it seemed like the source/original guest was seeing the extra volume as "internal" and perhaps doing something different with encryption "keybag" than it would an external? Or… something? Basically I could get a second "disk1.img" to work fine as a Time Machine volume within the first VM. But then after shutting down the first and adding the same "disk1.img" to the second VM, it would see the block drive but refuse to mount or "first aid" the APFS container within, not ever prompting for password just going straight to cryptic errors like:

com.apple.DiskManagement.disenter error 49244

and:

error: container keybag (101286206+1): unlock records entry 0 contains invalid range 218373742+1 error: volume keybag (218373742+1): failed to get keybag: Device not configured Encryption key structures are invalid.

didn't fully troubleshoot but it just didn't want to use the drive. My wild guess is that internal APFS drives get encrypted directly to the host CPU or something, whereas external can be moved between machines, but who knows…?)

NaanProphet commented 6 months ago

Hi @natevw thanks for your post! I found it helpful trying to resize a Mac OS 14 image myself.

By temporarily setting both the old and a clean-install new VM to "bridge" networking mode, I was able to use Migration Assistant to transfer over earlier work/settings from my original guest, into a new guest with a larger disk.

It turns out some of the software I had licensed didn't work well with the migration assistant transfer. So what I did instead was resize the main partition.

I don't think I can resize the partition for the main APFS container, without first moving or removing the recovery partition.

Exactly, so I went ahead and deleted the recovery partition. Here's exactly what I did:

  1. Install qemu-img using brew install qemu-img and then this command worked perfectly:
  2. Resize the IMG like you qemu-img resize -f raw <path-to-my>.img +30G
  3. Right click the VM and select Run Recovery. Boot into MacOS recovery mode.
  4. Disable System Integrity Protection in Terminal with crsutil disable
  5. Reboot normally
  6. Open Terminal and delete the recovery partition via diskutil eraseVolume APFS Blank [RECOVERY IDENTIFIER] frees up about 5.4 GB
  7. Use the Disk Utility GUI to resize the main data partition!

What's cool is I can still boot into Recovery mode using UTM, because the initial 500 MB partition is still there. I figure this is a VM, so do I really need the 5.4 GB Apple_APFS_Recovery partition? Probably not. Hope this helps!

Special thanks to:

barak-kalai commented 6 months ago

I followed the steps above and I can extend the the UTM image using gemu-img I then disable in recovery mode I erased the recovery partition In disk utility it dosen'tl let me extend the partition trying to "convert" the free space to a partition fails "remove free"((null)) and grow container disk4 "xxx" (disk0s2) the new size must be different than the existing size(-69743) trying to add a partition it only allows me to add 2g out of 85.9g

rubentsirunyan commented 5 months ago

@barak-kalai , try repairing the internal disk before resizing the synthesized. Something like this:

  1. diskutil repairdisk disk0
  2. diskutil apfs resizeContainer disk4
galaxy4public commented 4 months ago

Why would you need any external tools for resizing if on macOS the image can be resized natively?

$ hdiutil resize -size 40g -imageonly -verbose 233788C6-C25E-4CE1-BE15-716CD2C80AA1.img 
DIBackingStoreInstantiatorProbe: interface  0, score      100, CBSDBackingStore
DIBackingStoreInstantiatorProbe: interface  1, score    -1000, CBundleBackingStore
DIBackingStoreInstantiatorProbe: interface  2, score    -1000, CRAMBackingStore
DIBackingStoreInstantiatorProbe: interface  3, score    -1000, CDevBackingStore
DIBackingStoreInstantiatorProbe: interface  4, score    -1000, CCURLBackingStore
DIBackingStoreInstantiatorProbe: interface  5, score    -1000, CVectoredBackingStore
DIFileEncodingInstantiatorProbe: interface  0, score    -1000, CEncryptedEncoding
DIFileEncodingInstantiatorProbe: interface  0, score    -1000, CUDIFEncoding
DIFileEncodingInstantiatorProbe: interface  0, score    -1000, CSegmentedUDIFEncoding
DIFileEncodingInstantiatorProbe: interface  1, score    -1000, CSegmentedUDIFRawEncoding
DIDiskImageInstantiatorProbe: interface  0, score    -1000, CUDIFDiskImage
DIDiskImageInstantiatorProbe: interface  1, score        0, CSparseBundleDiskImage
DIDiskImageInstantiatorProbe: interface  2, score        0, CSparseDiskImage
DIDiskImageInstantiatorProbe: interface  3, score      100, CRawDiskImage
DIDiskImageInstantiatorProbe: interface  5, score     -100, CShadowedDiskImage
DIDiskImageInstantiatorProbe: interface  6, score     -100, CWrappedDiskImage
DIDiskImageNewWithBackingStore: CRawDiskImage
DIDiskImageNewWithBackingStore: instantiator returned 0
hdiutil: resize: completed successfully

So, basically, I did the following:

  1. located VM's image in UTM by right-clicking on the VM and selecting "Show in Finder"
  2. resized the image using hdiutil resize -size 40g -imageonly -verbose <name_of_the_image_file.img>
  3. closed and re-opened UTM, so it re-read the VM config, then started the VM in the recovery mode
  4. in the recovery mode, went to the Utilities menu, launched Terminal, and executed crsutil disable
  5. rebooted into the normal mode by using the apple menu -> Reboot
  6. once booted, used diskutil list to get the list of the containers (the first block in the output) and located the Apple_APFS_Recovery container
  7. removed the recovery container with diskutil apfs deleteContainer <id_of_the_recovery_container> (note that there are two recoveries - one is the 5.4GB container and another is the recovery volume -- you need to ensure that you are removing the container and not the volume)
  8. finally, resize the APFS container with your system volumes with diskutil apfs resizeContainer <id_of_the_synthetic_container> 0 (In my case it was disk3, it is the only synthesised disk you should have at this point)
  9. shutdown the VM, and start it in the recovery mode (it will take considerably longer this time due to the missing recovery container), then execute csrutil enable in the Terminal to re-enable the protection.
  10. Shutdown the VM, then start it normally -- you should be all set.
cdavidc commented 4 months ago

Please note that the steps below help if you want to recover the 5.4 GB from the Apple_APFS_Recovery partition to make your images as optimized as possible, but further OS update will probably fail. If you want to preserve your ability to upgrade, see the next comment.

Thanks @galaxy4public. I think everything can be done directly from recovery (at least it worked well for me).

In that case the simplified steps are:

  1. located VM's image in UTM by right-clicking on the VM and selecting "Show in Finder"
  2. resized the image using hdiutil resize -size 40g -imageonly -verbose <name_of_the_image_file.img>
  3. closed and re-opened UTM, so it re-read the VM config, then started the VM in the recovery mode
  4. in the recovery mode, went to the Utilities menu, launched Terminal
  5. used diskutil list to get the list of the containers (the first block in the output) and located the Apple_APFS_Recovery container
  6. removed the recovery container with diskutil apfs deleteContainer <id_of_the_Apple_APFS_Recovery_container> (note that there are two recoveries - one is the 5.4GB container and another is the recovery volume -- you need to ensure that you are removing the container and not the volume)
  7. finally, resize the APFS container with your system volumes with diskutil apfs resizeContainer <id_of_the_Apple_APFS_container> 0 (it should be the container that was right above the Apple_APFS_Recovery container you deleted at the previous step)
  8. shutdown the VM, then start it normally -- you should be all set.
cdavidc commented 4 months ago

If you want to be able to increase the size of your disk image while retaining the ability to upgrade your OS, the Apple_APFS_Recovery partition must be relocated to the end of the disk before expanding the main Apple_APFS container.

So far the only ready-made solution I have found is the Tart packer plugin from the Tart project by cirruslabs which allows for automated VM creation and has the option to automatically resize disk images (see the recovery_partition = "relocate" option).

More information on the projects can be found below: https://github.com/cirruslabs/tart https://github.com/cirruslabs/packer-plugin-tart/tree/main

Since I wanted to continue using UTM and simply needed a standalone tool to relocate the partition, I created a small tool based on their code. Understand that it come with no guarantee, but the code is available here: https://gist.github.com/cdavidc/3509ceefba518f20d1c123b6435407d6

To build it, install go (with brew for instance) and execute the few commands listed in the comments at the beginning of the file.

Considering that, the full instructions are:

  1. Locate the VM image in UTM by right-clicking on the VM and select "Show in Finder"
  2. Resize the image using hdiutil resize -size <new_size_in_GB>g -imageonly -verbose <full_path_and_name_of_the_image_file>
  3. cd to where you built the RelocatePartition tool and run ./RelocatePartition <full_path_and_name_of_the_image_file>
  4. Close and re-open UTM, so it re-reads the VM config, then start the VM in the recovery mode
  5. Use diskutil list to get the list of the containers (the first block in the output) and located the Apple_APFS container
  6. Resize the APFS container with diskutil apfs resizeContainer <id_of_the_Apple_APFS_container> 0
  7. Restart the VM