This week I ordered a new computer to replace one of my private machines at home. Because I'm still indecisive what to do with the old one and selling it is one option, I thought it might be a good idea to erase all the old data on the two hard disks. The problem is that format does not really do this, leaving the possibility to potentially restore files later. So, I wrote this little script that you would not want to use at an intelligence service (you will see why later) but which should be sufficient for home use. Because it is a command line script, it must be run by CScript.
CScript Overwrite.vbs [path] [filename]
The basic idea is to create a file which consumes all free space on a partition and contains only random data. So, my script starts with some default settings and then calls
Const sDefaultDir = "C:\"
Const sDefaultFilename = "overwrite.garbage"
Const lStartBlockSize = 32768
Option Explicit forces you to explicitly declare variables you want to use. This helps to avoid using variables you accidentally created by a typo, and those errors can be really hard to debug. The Constants contain the default directory, default filename, and the block size. Directory and filename should be self-explanatory. The block size defines how many bytes are written with each call of
CreateGarbageFile. After experimenting with some block sizes, I decided to use a 32 kilobyte block (= 32 * 1024 bytes = 215 bytes) because a larger block would take too much time to be created on a 200 MHz processor, and a 32 Kb block takes a few seconds to be created even on a 2 GHz processor.
Sub Main does all the work regarding the fulfillment of the prerequisites and also starts off with some local declarations and initializations.
sFilename will store the actual values which can be eventually the default values or values passed as command line parameters. The other variables will be used for various objects.
ShowStartMsg is a simple method that just uses
WScript.Echo to display a static start-up message.
The last two lines are more interesting. The
Arguments property of the
WScript object returns a collection of the command line parameters. A
FileSystemObject object is needed for all the file I/O operations like processing paths, dealing with drives, or creating a file. The object has to be created prior to use, by calling
CreateObject which is also a method of
Dim sPath, sFilename
Dim oArgs, oFS, oDrive, oRegExp, oMatches
Set oArgs = WScript.Arguments
Set oFS = CreateObject("Scripting.FileSystemObject")
The first check I perform is whether there is only one command line parameter and this parameter equals
/?. I get the number of elements in the
WshArguments collection by calling
Count, and access elements by using their index (starting with 0), so usage does not differ from any other collection. If both conditions are true,
ShowHelp is called to display some information on how to use the script. The parameter value
ShowHelp to cancel the execution of the script by calling
If oArgs.Count = 1 Then
If oArgs(0) = "/?" Then
Then I check if more than two parameters were passed from the command line. Because the script supports only two parameters (which are both optional), this would be an invalid input. The
Sub ShowMsg is defined as
Sub ShowMsg(sMsg, bShowHelpHint, bExit) where
sMsg is a string to be displayed on the console.
bShowHelpHint determines whether a hint on how to start Overwrite.vbs to get help will be shown, and
bExit determines if the script should quit after the message was displayed.
If oArgs.Count > 2 Then
ShowMsg "ERROR: Invalid command line parameters" & _
" (too many parameters specified)", true, true
After that, I check if a path has been provided by the user. If he or she did, I call
GetAbsolutePathName for further use.
GetAbsolutePathName translates relative path names into absolute path names. It does not matter for making the script work but always using absolute path names when telling the user what is done makes the script's output unambiguous. If no path was specified, I initialize
sPath with an empty string.
If oArgs.Count > 0 Then
sPath = oFS.GetAbsolutePathName(oArgs(0))
sPath = ""
The next step is to verify that the given path exists.
FolderExists does this for me. All I need to do is to pass the path I want to verify, in this case the content of
sPath. A backslash is amended to a user-specified path in case it was omitted by the user. This allows getting a valid path to the file we are going to write by combining the path and the filename (
sPath & sFilename).
If oFS.FolderExists(sPath) Then
WScript.Echo "Checking folder " & Chr(34) & sPath & Chr(34) & ": OK"
If Right(sPath, 1) <> "\" Then
sPath = sPath & "\"
WScript.Echo "Checking folder " & Chr(34) & sPath & Chr(34) & ": FAILED"
sPath = sDefaultDir
WScript.Echo "INFO: Using default folder " & Chr(34) & sPath & Chr(34)
After we know that the path is okay, the filename needs verification. This is done in three steps. The first step is to analyze the command line parameters. If a filename was specified by the user and it is not empty, this filename is copied into
sFilename (an empty filename provided by the user is treated as an error condition). If no filename was specified, the default filename (see Declarations) is copied into
If oArgs.Count = 2 Then
sFilename = oArgs(1)
If sFilename = "" Then
ShowMsg "ERROR: Filename must not be empty", true, true
sFilename = sDefaultFilename
WScript.Echo "INFO: Using default filename " & Chr(34) & sFilename & Chr(34)
Second, we need to ensure that the filename does not contain any invalid characters. This is done by using a regular expression. After creating an instance of the
RegExp class, I set the
Pattern property to my regular expression which contains all the forbidden characters. Then, I call
Execute and pass
sFilename as the parameter. The return value is a
Matches collection. Because I just want to know if my filename contains any invalid characters, I don't bother with checking the collection's content but only with how many elements it contains. If it does not contain any elements at all, the filename is valid.
Set oRegExp = new RegExp
oRegExp.Pattern = "[\\\/\:\*\?\" & Chr(34) & "\<\>\|]"
Set oMatches = oRegExp.Execute(sFilename)
If oMatches.Count = 0 Then
WScript.Echo "Validating filename: OK"
WScript.Echo "Validating filename: FAILED"
ShowMsg "ERROR: Filename must not contain the following characters:"_
& " \ / : * ? " & Chr(34) & " < > |", true, true
Because the user might enter the name of an existing file and I really don't want to delete user data or program files, I finally make sure that the specified file does not exist by calling the
FileExists method of
If oFS.FileExists(sPath & sFilename) = False Then
WScript.Echo "Ensuring that file " & Chr(34) & sFilename & Chr(34) &_
" does not exist: OK"
WScript.Echo "Ensuring that file " & Chr(34) & sFilename & Chr(34) &_
" does not exist: FAILED"
ShowMsg "ERROR: File " & Chr(34) & _
sPath & sFilename & Chr(34) & " already exists", true, true
Now, I make sure that my target is a NTFS partition. Why is that? Because I wanted to write a simple script which just writes one big file. Using FAT file systems implies a maximum file size of 4 GB (in case of FAT32). The free space however might be larger than that and I didn't want to create multiple files (in fact I don't even use FAT file systems anymore) to keep the script as simple as possible.
To get this information, we need a
Drive object. And to get that, we simply call
GetDrive and pass the return value of
GetDriveName as its parameter (passing the whole path would lead to an exception when it does not point to the root directory). Then we just check if the
FileSystem property is set to NTFS.
Set oDrive = oFS.GetDrive(oFS.GetDriveName(sPath))
If UCase(oDrive.FileSystem) = "NTFS" Then
WScript.Echo "Checking for NTFS: OK"
WScript.Echo "Checking for NTFS: FAILED"
ShowMsg "ERROR: " & oDrive.FileSystem & _
" file system not supported", true, true
The last thing we want to prevent is that someone starts writing random data over a network. Therefore, we use the
DriveType property of our
Drive object to get the drive type, and only proceed if it is a fixed or a removable drive.
Select Case oDrive.DriveType
Case 1, 2
WScript.Echo "Checking drive type: OK"
WScript.Echo "Checking drive type: FAILED"
Select Case oDrive.DriveType
ShowMsg "ERROR: Network drives are not supported", true, true
ShowMsg "ERROR: CD-ROM drives are not supported", true, true
ShowMsg "ERROR: RAM Disk drives are not supported", true, true
ShowMsg "ERROR: Unkown drives are not supported", true, true
The last check eventually verifies that there is any free space we are allowed to write to. This is not necessarily the free space of the partition if quotas are active. This information is returned by the
If oDrive.FreeSpace > 0 Then
WScript.Echo "Checking for free space: OK"
WScript.Echo "Checking for free space: FAILED"
WScript.Echo "INFO: No free space available (no action required)"
ShowMsg "INFO: Exiting Overwrite Script...", false, true
Now that we know that everything is in place, we can actually start filling the drive with junk.
Main only writes some output to the console, calls
CreateGarbageFile, and deletes the garbage file by calling
WScript.Echo "Creating garbage file " & Chr(34) & _
sPath & sFilename & Chr(34) & "..."
CreateGarbageFile sPath & sFilename, oFS
WScript.Echo "Garbage file successfully created!"
WScript.Echo "INFO: " & oDrive.AvailableSpace & _
" byte(s) remained which could not be overwritten"
WScript.Echo "Deleting garbage file..."
oFS.DeleteFile sPath & sFilename
WScript.Echo "Garbage file successfully deleted!"
WScript.Echo "Exiting Overwrite Script..."
Sub CreateGarbageFile(sAbsFilename, oFS)
CreateGarbageFile expects two parameters.
sAbsFilename is the filename including the full path and
oFS is a
FileSystemObject object. The local variable
bSngByteBlock is set to
true when the function enters the single byte block mode, while
sBlock is the string that is written to disk. The
Drive object was introduced when discussing
oFile is a
TextStream object. This object is used to actually write data to disk and is created by calling the
CreateTextFile method of the
FileSystemObject class. The first parameter of that call is the filename. The second one tells the function not to overwrite files and the third one means that 8-bit ASCII will be used (the alternative would be Unicode).
Dim oFile, oDrive
bSngByteBlock = false
Set oDrive = oFS.GetDrive(oFS.GetDriveName(sAbsFilename))
Set oFile = oFS.CreateTextFile(sAbsFilename, false, false)
Now, a new data block is created by calling
GenGargabeBlock using the default block size (see Declarations). The second parameter allows the function to write output to the console. Then error handling is turned on. This is necessary because there may be problems when the disk is almost full and only a few kilobytes of free space remains. In this situation, an attempt to write data using the
TextStream object may fail. Therefore, an error handler is needed. Furthermore, the single byte block allows writing as much data to disk as possible (or in other words, help to delay the occurrence of the exception).
sBlock = GenGarbageBlock(lStartBlockSize, true)
On Error Resume Next
A loop is used to continuously write the junk block into the file. The stop criterion is met when there is no free disk space available anymore. However, there are two exceptions. When there is still disk space left but it is smaller than our block of junk, the function switches to single byte block mode. It will then create a new one byte string with every cycle and write it to disk. As described before, an exception may occur when writing, although there is still disk space left. In this case, the
Err object is used to determine if an exception occurred. If there was an exception, a message is displayed and the loop is terminated.
Do While oDrive.FreeSpace > 0
If oDrive.FreeSpace < lStartBlockSize Then
If bSngByteBlock = false Then
WScript.Echo "INFO: Falling back to single byte block"
bSngByteBlock = true
sBlock = GenGarbageBlock(1, false)
If Err.Number <> 0 Then
WScript.Echo "WARNING: Error " & Chr(34) & Err.Description & Chr(34) & " ("_
& Err.Number & ") occured while writing garbage file"
The last task is to clean up. That means deactivating error handling and closing our beloved garbage file.
On Error GoTo 0
Function GenGarbageBlock(lSize, bShowMsg)
GenGarbageBlock is responsible for creating a string with random information. Of course, the function
Rnd only returns pseudo random numbers. In fact, it uses an algorithm that returns something that looks random, but if you always use the same initial value, you will always get the same numerical series.
If bShowMsg = True Then
WScript.Echo "Creating new random data block (" & lSize & " bytes)..."
GenGarbageBlock = ""
For lCounter = 1 To lSize
GenGarbageBlock = GenGarbageBlock & Chr(Int ((255 + 1) * Rnd))
If bShowMsg = True Then
WScript.Echo "Data block complete"
WScript.Echo "Continue writing garbage file..."