Saturday, January 29, 2011

Creating disk fragmentation in virtual disks

Yes, you read that right. I WANT to fragment a hard drive. I want to do it well. I want it relatively quickly (one day vs. three years). And, you know what, it isn't as easy as you might expect.

I mean, I could install a Windows XP virtual machine, and then update it. But, it is use over time that causes fragmentation. The writing and deleting of files. The intermix of small and large files. Unless you have an old server sitting around you don't have it.

Oh, and I want to do this in a virtual machine. Hmm...So that means I can only go backward about six years at the very most. And who has had a single virtual machine running for six years? (at least that I can get my hands on..)

Needless to say, I have been busy the past couple days with PowerShell.

My theory is this: Write a bunch of little files. Randomly delete them. Write some big files. Randomly delete them and check the result.

My results have been promising.  I may need to tweak it a bit to be sure to break near non-standard disk sizing increments.  But for a first try it was not too bad.  On a side note.  Nothing can simulate fragmentation like a PC that has been running in continuous service for many years, without a good 3rd party defragmentation.

Here is my PowerShell script:

# This is a script designed to highly fragment a system disk in one pass.
# The theory is to nearly fill the disk with 128kb files, then to delete
# one-half of those files, then populate the disk with 2Mb files, then remove a portion of those.
#
# I am going to use randomization in the delete functions, therefore it is unknown exactly
# how many files might be deleted, because an empty array location might be chosen.
# Initiate the random number function
$Random=New-Object System.Random (get-date).millisecond
# Get the System Disk
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive}
# determine 10%
$tenPercent = $systemDisk.Size / 10
# Move to to the system Temp folder where we will do the workset-Location $Env:Temp
################################################################################ Write lots of 128kb files
###############################################################################
write-host "Write lots of 128kb files"
# An array for the names
$128kNames = @()
#Create the sample 128k file
$fileToCopy = "128kSample.txt"
for ( $i = 0; $i -lt 21843; $i++) { "1" | Out-File $fileToCopy -append }
While ($systemDisk.FreeSpace -gt $tenPercent) { Clear-Host Write-Host "Writing small files" Write-Host "Remaining free space is: " $systemDisk.FreeSpace Write-Host "Cycle: " $128kNames.Count

# Set the file name
$file = [string]$systemDisk.FreeSpace +".txt" copy $fileToCopy $file
# Write the file name to the array
$128kNames = $128kNames + $file


# Requery the disk $systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive} }
################################################################################ Delete half of the 128kb files
###############################################################################
write-host "Delete half of the 128kb files"
# 1/2 of names in the array
$half = $128kNames.Count / 2
While ($half -gt 0) {
Clear-Host Write-Host "Deleting small files" Write-Host "Remaining free space is: " $systemDisk.FreeSpace Write-Host "Countdown: " $half
$rndName = $128kNames[$Random.Next(0, $128kNames.Count)]
Remove-Item $rndName $half-- }

# Requery the disk
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive}
# Clear the small name array
$128kNames = 0
################################################################################ Write lots of 2Mb files
###############################################################################
write-host "Write lots of 2Mb files"
# An array for the names
$2mbNames = @()
#Create the sample 2Mb file
$fileToCopy = "2MbSample.txt"
for ( $i = 0; $i -lt 349512; $i++) { "0" | Out-File $fileToCopy -append }
While ($systemDisk.FreeSpace -gt $tenPercent) {
Clear-Host Write-Host "Writing large files" Write-Host "Remaining free space is: " $systemDisk.FreeSpace Write-Host "Cycle: " $2MbNames.Count

# Set the file name
$file = [string]$systemDisk.FreeSpace +"2mb.txt" copy $fileToCopy $file
# Write the file name to the array
$2mbNames = $2mbNames + $file
# Requery the disk
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive} }
################################################################################ Delete half of the 2Mb files
###############################################################################
write-host "Delete half of the 2Mb files"
# 1/2 of names in the array
$half = $2mbNames.Count / 2
While ($half -gt 0) {
Clear-Host Write-Host "Deleting large files" Write-Host "Remaining free space is: " $systemDisk.FreeSpace Write-Host "Countdown: " $half
$rndName = $2mbNames[$Random.Next(0, $2mbNames.Count)]
Remove-Item $rndName $half-- }
Clear-HostWrite-Host "Finally done"

# What is the damage?
# THIS DOES NOT WORK ON WIN XP AND EARLIER - THIS CLASS IS NOT AVAILABLE
# $sysVolume = get-wmiObject win32_volume | where {$_.DriveLetter -eq $Env:SystemDrive}

# $sysVolume.DefragAnalysis

3 comments:

JP said...

Hi, I'm trying to perform some testing on fragmentation, and found your script, but it is not working well, it just created the sample128k file and other with random name, but no more than that.

Also, i would like to point it to another drive other than the system, so i changed
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive} }
to
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq "E:"}

is this correct? thanks in advance

JP

JP said...

Hi, I'm trying to perform some testing on fragmentation, and found your script, but it is not working well, it just created the sample128k file and other with random name, but no more than that.

Also, i would like to point it to another drive other than the system, so i changed
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq $Env:SystemDrive} }
to
$systemDisk = get-wmiObject win32_LogicalDisk | where {$_.DeviceId -eq "E:"}

is this correct? thanks in advance

JP

BrianEh said...

While you can't beat the impact of a really old drive, this only works if it is run repeatedly with files of varying sizes.
And even then it is not as good as I would have liked (I admit).

A while back there was a dirty way to create a VHD very quickly. Basically wrapping physical sector space with the VHD header and footer and taking whatever happened to be on the physical disk.

The security implication is that the physical disk sector contents are now in the VHD. But there are test possibilities there as well.

There are some folks that are deep into this that can really make this happen in strange ways, but I have been unable to get them to share. They are deep storage and file system nuts.

I think I am going to ping one and see if he can help. You have me interested in this again.