How to resize a Windows VM image with virt-resize

I recently had a Windows 7 Virtual Machine stored on an undersized qcow2 file. This post steps through the simplest way that I know to produce a new, bigger disk and expand the filesystem onto it.

Empty out the empty space

Because the guest VM is stored on a QCOW2 file, we can recover un-used space on disk by zeroing it out now. Download the sdelete utility from Microsoft and run it on the system.

sdelete -z

One this is done, power off the guest.

Assuming the host is linux, you need the qemu-utls and libguestfs-tools packages to follow these steps. On Debian-

apt-get install libguestfs-tools qemu-utls

Move the VM image to a new filename and inspect it.

mv windows.img windows.img.bak

The file command indicates that the disk is about 30GB expanded.

$ file windows.img.bak 
windows.img.bak: QEMU QCOW Image (v3), 32212254720 bytes

The qemu-img command shows that the disk is 83% full:

$ qemu-img check windows.img.bak 
No errors were found on the image.
411337/491520 = 83.69% allocated, 5.66% fragmented, 0.00% compressed clusters
Image end offset: 26961969152

Check out your FS names, note that /dev/sda2 is the disk we want to up-size in this case

$ virt-filesystems -a windows.img -l
Name       Type        VFS   Label            Size         Parent
/dev/sda1  filesystem  ntfs  System Reserved  104857600    -
/dev/sda2  filesystem  ntfs  -                32105299968  -

Make a new, bigger disk image

Create a new disk of the desired size. In my case, 50G is sufficient:

$ qemu-img create -f qcow2 windows.img 50G
Formatting 'windows.img', fmt=qcow2 size=53687091200 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16

The file command shows that this new empty disk image is larger than the old image.

$ file windows.img
windows.img: QEMU QCOW Image (v3), 53687091200 bytes

Copy the old disk to the new one. The --expand option names a partition which will be grown to fill the extra space.

virt-resize --expand /dev/sda2 windows.img.bak windows.img

The virt-resize command shows a progress bar while it works, and zero-blocks will be reclaimed as a result of the output format:

2016-04-disk01

The final line of output suggests holding on to your backup until you’ve checked it, which is wise:

Resize operation completed with no errors. Before deleting the old disk,
carefully check that the resized disk boots and works correctly.

Check that the new disk is valid and contains partitions at the expected size:

$ virt-filesystems -a windows.img -l
Name       Type        VFS   Label            Size         Parent
/dev/sda1  filesystem  ntfs  System Reserved  104857600    -
/dev/sda2  filesystem  ntfs  -                53579939840  -

Boot up the guest

When the machine boots up, you may get a disk check prompt. Because the console I was using triggered the ‘Press any key to cancel’ prompt, I had to reboot and leave the console disconnected in order for the check to start.

2016-04-disk02

2016-04-disk03

After booting, the C:\ drive should display at its new size:

2016-04-disk04

It’s time to migrate away from Outlook Express

Outlook Express is obsolete, so you need to migrate if you’re still using it. This post is a quick guide to saving your local data so that you can jump ship.

If you are keen on desktop-based email, then there are only two real contenders for a replacement mail client:

  • Mozilla Thunderbird (suggested).
  • Windows Mail.

So where is all my data?

The script below is a Windows bat script for backing up an Outlook Express setup. Just fill in the Identity variable and run it from anywhere to produce a folder containing the Outlook saved emails and contacts.

You can find your identity string as a folder name in your profile path, under Local Settings\Application Data\Identities\. The profile path for a user called bob would usually be in a folder like C:\Documents and Settings\bob or C:\Users\bob.

These files can be read by both of the suggested replacements, so a good transition might be:

  1. Make a backup.
  2. Install an alternative & set it up.
  3. Try to import as much as you can.
  4. Once you’re happy, uninstall Outlook Express, or at least delete the shortcuts to it.
@echo off
ECHO -------------------------------------------------------------------------------
SET BACKUPDIR="%username% - Outlook Backup"
SET IDENTITY={AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}
ECHO Backing up outlook files to: %backupdir%
ECHO --------------------------------------------------------------------------------

ECHO - Clearing backup location... (1 of 3) 
DEL /F /S /Q %backupdir% > NUL 2>&1
MKDIR %backupdir% 2> NUL
ECHO - - Done

ECHO - Copying emails... (2 of 3) 
MKDIR %backupdir%\Emails 2> NUL
XCOPY /E /H /C /R /Y "%userprofile%\Local Settings\Application Data\Identities\%IDENTITY%\Microsoft\Outlook Express" %backupdir%\Emails > NUL
ECHO - - Done

ECHO - Copying address book... (3 of 3)
MKDIR %backupdir%\AddressBook 2> NUL
XCOPY /E /H /C /R /Y "%appdata%\Microsoft\Address Book" %backupdir%\AddressBook > NUL
ECHO - - Done

ECHO - Completed 3 of 3 tasks.
ECHO -------------------------------------------------------------------------------
ECHO The backup is complete.
ECHO Please copy %BACKUPDIR% to external storage.
ECHO --------------------------------------------------------------------------------
PAUSE

How to create an animated GIF from a series of images

Sometimes, you end up with a folder full of images, which you want to animate. With the open source ImageMagick tool, this is easy on the command line:

animate *.png

This will show you all of the PNG files in the folder in quick succession, like a flip book.

ImageMagick works on just about any OS. For Linux users, the package is generally imagemagick or ImageMagick:

sudo apt-get install imagemagick
yum install ImageMagick

But this blog post is about animated GIFs, so lets make one of those. This is a compact way to combine images (here and here for examples in context), gives you a re-usable at-a-glance illustration of something that changes over time.

Example from an older post:

2015-04-tetris

The steps to make a good conversion command are:

  1. Check that alphabetically, your images are in order. If not, rename them:
    echo *
  2. Convert them to a GIF a few times, and find the delay that suits you (hundredths of a second between frames)
    convert -delay 80 *.png animated.gif
  3. Choose an output size (width x height):
    convert -resize 415x -delay 80 *.png animated.gif
  4. Compress with -Layers Optimize for a smaller file:
    convert -resize 415x -delay 80 *.png -layers Optimize animated.gif

Notes

  • Generated thumbnails usually take the first frame only, which is why we ask Imagemagick to resize it (WordPress users: Choose “Full Size”).
  • To pause at the start of the loop for a moment, just copy the first image a few times.

Getting a USB receipt printer working on Windows

Note:This post is a Windows adaptation of an earlier post, Getting a USB receipt printer working on Linux, mainly in response to these questions.

In this post, I’ll step through how to get a USB thermal receipt printer appearing on Windows. The aim of this is to be able to send raw text to the printer, so that we can point a driver such as escpos-php at it. The printer tested here is once again this Epson TM-T20:

2015-03-printer-back
2015-03-printer-top

The directions below are for Windows 7, so your mileage may vary if you are on an older or newer version.

If you have issues following these steps, make sure you can locate your printer in Device Manager, and that it has “USB Print Support”.

Add the printer

Find Devices and Printers and click Add a Printer.
2015-04-windowsusb-01

2015-04-windowsusb-02

Add it as a Local printer, using the USB virtual port, probably USB0001:

2015-04-windowsusb-03
2015-04-windowsusb-04

Use the Generic / Text Only driver.

2015-04-windowsusb-05

Name the printer whatever you like, and then share it under the same name:

2015-04-windowsusb-06
2015-04-windowsusb-07

At this point, it should pop up in the window in the background, and also prompt you to Print a test page.

2015-04-windowsusb-08
2015-04-windowsusb-09

The test print is plain-text, and depending on your printer, will look something like this:

2015-04-windowsusb-10

Finally, you need to verify that your printer can be accessed locally, by typing \\localhost into Windows Explorer. If all goes to plan, you will see the new printer there too:

2015-04-windowsusb-11

Run a command-line test print

We now know that your printer is working, and can be accessed via its share name (even locally).

Test printing from the command-line. Fire up cmd.exe and try to send it some text to verify that it’s working:

echo "Hello World" > testfile
print /D:"\\%COMPUTERNAME%\Receipt Printer" testfile
del testfile

Printing something useful

This is where you start to see real results. Receipt printers are not just for printing plain-text. Many of them support a standard called ESC/POS, which contains formatting commands.

The snippet below, from this earlier post, generates some basic ESC/POS commands.

Install PHP if you don’t have it already, and call the below code foo.php:

<?php
/* ASCII constants */
const ESC = "\x1b";
const GS="\x1d";
const NUL="\x00";

/* Output an example receipt */
echo ESC."@"; // Reset to defaults
echo ESC."E".chr(1); // Bold
echo "FOO CORP Ltd.\n"; // Company
echo ESC."E".chr(0); // Not Bold
echo ESC."d".chr(1); // Blank line
echo "Receipt for whatever\n"; // Print text
echo ESC."d".chr(4); // 4 Blank lines

/* Bar-code at the end */
echo ESC."a".chr(1); // Centered printing
echo GS."k".chr(4)."987654321".NUL; // Print barcode
echo ESC."d".chr(1); // Blank line
echo "987654321\n"; // Print number
echo GS."V\x41".chr(3); // Cut
exit(0);

You would send generated commands to the printer like this:

php foo.php > testfile
print /D:"\\%COMPUTERNAME%\Receipt Printer" testfile
rm testfile

Scaling this up

The correct ESC/POS codes are quite tricky to generate with manually, which is why I put together the escpos-php driver. You can find more information on that at:

A simple “Hello World” receipt to your Windows shared printer would be scripted as (call this one foo2.php):

<?php
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\WindowsPrintConnector;

try {
	// Enter the share name for your USB printer here
	$connector = new WindowsPrintConnector("Receipt Printer");
	$printer = new Printer($connector);

	/* Print a "Hello world" receipt" */
	$printer -> text("Hello World!\n");
	$printer -> cut();
	
	/* Close printer */
	$printer -> close();
} catch(Exception $e) {
	echo "Couldn't print to this printer: " . $e -> getMessage() . "\n";
}

This would be sent to the printer by loading it from the web, or running the script on the command-line:

php foo2.php

The full ESC/POS snippet with formatting, coded up with escpos-php, would look like this (call this one foo3.php):

<?php
require __DIR__ . '/autoload.php';
use Mike42\Escpos\Printer;
use Mike42\Escpos\PrintConnectors\WindowsPrintConnector;
try {
	// Enter the share name for your USB printer here
	$connector = new WindowsPrintConnector("Receipt Printer");
	$printer = new Printer($connector);

	/* Print some bold text */
	$printer -> setEmphasis(true);
	$printer -> text("FOO CORP Ltd.\n");
	$printer -> setEmphasis(false);
	$printer -> feed();
	$printer -> text("Receipt for whatever\n");
	$printer -> feed(4);

	/* Bar-code at the end */
	$printer -> setJustification(Printer::JUSTIFY_CENTER);
	$printer -> barcode("987654321");
	
	/* Close printer */
	$printer -> close();
} catch(Exception $e) {
	echo "Couldn't print to this printer: " . $e -> getMessage() . "\n";
}

And again, this could be executed by loading the page through the web, or invoking the command directly:

php foo3.php

How to empty your local user account

If you’re not going to use a user account on your computer again, but can’t delete it for some reason, then emptying it is the next best thing to do.

Note: Save anything you want to keep before you start deleting things. These are destructive commands which delete all of the files and settings in the current user’s profile. If you are at all unsure, consider using a file browser to clear out the profile instead.

Windows:

cd %USERPROFILE%
del /A / F /Q /S .

Linux or Mac:

cd ~
rm -Rf .

This will make sure that the disused account no-longer wastes any disk space.

Crash course: Run Windows on desktop Linux

Sometimes, you need to use a tricky windows-only proprietary program on a GNU/Linux desktop. If you have a Windows install disk and licence at your disposal, then this post will show you how to get a Windows environment running without dual-booting.

The host here is a Debian box, and the guest is running Windows 7. The instructions will work with slight modifications for any mix of GNU/Linux and Windows

On the desktop, some things are not as important as the server world. Some things are excluded for simplicity: network bridging, para-virtualised disks, migration between hosts, and disk replication.

Software setup

Everything required from the host machine can be pulled in via Debian’s qemu-kvm package.

sudo apt-get install qemu-kvm

Install

Prepare a disk image for Windows. The qcow2 format is suggested for the desktop as it will not expand the file to the full size until the guest uses the space:

qemu-img create -f qcow2 windows.img 30G

Launch the Windows installer in KVM with a command that looks something like this:

kvm -hda windows.img --cdrom windows-install-disc.iso -vga std -localtime -net nic,model=ne2k_pci -m 2048

Note the -m option is the number of megabytes of RAM to allocate. You can set it a little lower if you don’t have much to spare, but if it’s too low you’ll get this screen:

2014-06-04-capture-noboot

If you have a physical disk but no .iso of it, then using the disk drive via --cdrom /dev/cdrom will work.

Install

If you have GNU/Linux, chances are you have installed an OS before. In case you haven’t seen the Windows 7 installer, the steps are below:

Select language, accept the licence agreement, choose the target disk, and let the files copy:

2014-06-04-capture-005
2014-06-04-capture-023
2014-06-04-capture-024
2014-06-04-capture-028
2014-06-04-capture-033
2014-06-04-capture-035
2014-06-04-capture-081
2014-06-04-capture-149

After reboot, enter the user details, licence key, update settings and timezone:

2014-06-04-capture-160
2014-06-04-capture-178
2014-06-04-capture-190
2014-06-04-capture-191
2014-06-04-capture-199
2014-06-04-capture-205
2014-06-04-capture-214
2014-06-04-capture-250
2014-06-04-capture-254
2014-06-04-capture-261

After another reboot, Windows is installed in the virtual machine:

2014-06-04-capture-263
2014-06-04-capture-265
2014-06-04-capture-277

Post-install tasks

The guest you have now will only run at standard VGA resolutions, and will probably not be networked. This section will show you how to fix that.

Network drivers

You will notice that we are launching the guest with -net nic,model=virtio. This means that we are using a virtual network card, rather than simulating a real one. You need to fetch a disk image with the latest binary drivers, which are best tracked down on linux-kvm.org via google.

Once you have the disk image in the same folder as your virtual machine, shut down and launch it with a CD:

kvm -hda windows.img --cdrom virtio-win-0.1-74.iso -vga std -localtime -net nic,model=ne2k_pci -m 2048

Under "My Computer" track down the "Device Manager", find your network card, and tell Windows to update the drivers. You can then point it to the CDROM’s "Win7" directory (or other one, if you are installing a different guest). After the network adapter is recognised, you will be connected automatically.

Note that you are using "user-mode" networking, which means you are on a simulated subnet, and can only communicate via TCP and UDP (ie, ping will not work). This can be a little slow, but will work on a laptop host whether plugged in or running on WiFi.

Remote desktop

You may also be annoyed by the screen resolution and mouse sensitivity having strange settings. The best way around this is not to fiddle with settings and drivers, but to enable remote desktop and log in via the network. This lets you use an arbitrary screen size, and match mouse speed to the host.

This is set up to run locally, so it is neither laggy nor a security issue, and makes it possible to leverage all RDP features.

First, in the guest, enable remote desktop using these Microsoft instructions.

Then shut down and boot up with the extra -redir tcp:3389::3389 option:

kvm -hda windows.img -vga std -localtime -net nic,model=ne2k_pci -m 2048 -redir tcp:3389::3389

On the host, wait for the guest to boot, then use rdesktop to log in:

rdestkop localhost

One this works, you can shut down and boot with the extra -nographic option to leave remote desktop as the only way to interact with the guest:

kvm -hda windows.img -vga std -localtime -net nic,model=ne2k_pci -m 2048 -nographic -redir tcp:3389::3389

The rdesktop tool supports sound, file and printer redirection. It can also run fullscreen when launched with -f. All the details are in man rdesktop

If you end up using the guest operating system more, it is worth investigating USB-redirection for any peripherals (printers or mobile phones), running a virtual sound card, or running SAMBA on the host to share files.

Why you should disable IPv6 on Windows

This post is mainly in response to (what is in my opinion) a piece of misinformation which I stumbled across today in this blog:

If you are running any Windows computer on an un-trusted network, then it is probably wide open to CVE-2010-4669. This means that a few thousand dodgy ICMPv6 packets could fill up its memory until it keels over and needs to be rebooted.

I’m not an advocate of Windows on servers, but it exists and can be made to crash less. If you don’t need IPv6, because you are behind an IPv4 NAT for example, you can just switch it off and bypass Microsoft’s poorly designed implementation altogether. To that end, here is a nice article that will get you depolying .reg files for that in a few minutes.

This is easy and I would recommend it. Contrary to the article above, your computer will work fine on an IPv4 network without IPv6. If disabling IPv6 breaks some application, then it probably wouldn’t have worked properly on your network anyway. What’s important is that the computer works!

A solid windows firewall configuration will also solve this, but involves leaving the vulnerable stack running. This is a decent security compromise, as it assumes that you will actually cover every possible attack scenario in your firewall rules.

Scripting Windows Shares

As much as I try to avoid it, sometimes I need to use Windows servers, and windows .bat files aren’t exactly the pinnacle of scripting languages. This post is about bulk-sharing home directories with consistent permissions.

As I’m a reformed Visual Basic programmer, so I decided to solve this in PHP rather than VB script.

This crude script will make a batch file to share every subdirectory (ie, hundreds of users’ home directories), and also delete desktop.ini from each of them them. Save the code below as magic.php, run it, and then run tricks.bat.

<?php
$stuff = whats_here();
$tricks = fopen("tricks.bat", "w");
foreach($stuff as $folder) {
	$line = do_things($folder);
	fwrite($tricks, $line);
}
fclose($tricks);

function do_things($folder) {
	$things .= ":: $folderrn";
	$things .= "net share $folder /DELETErn";
	$things .= "net share $folder=".getcwd()."\$folder /GRANT:EVERYONE,FULLrn";
	$things .= "del /Q $folderdesktop.inirnrn";
	return $things;
}

function whats_here() {
	/* List directories in this one */
	$here = opendir(getcwd());
	$dir  = array();
	while($kid = readdir($here)) {
		if(is_dir($kid) && $kid != "." && $kid != "..") {
			$dir[] = $kid;
		}
	}
	closedir($here);
	return $dir;
}?>

I’ve heard that Windows PowerShell is pretty useful once you get used to it, but Windows sysadmins seem to dislike scripting as much as I dislike using repetitious GUI interfaces. Oh well, a couple of PHP scripts wont hurt. :)