/* Copyright (c) 2005, Damon Hart-Davis All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.io.*; import java.security.*; /**Java utility to overwrite free space in a filesystem. This is a simple Java utility to attempt to overwrite all free space on a disc several times with random data and then release it.
If you have just zapped a bunch of files that you want removed then this program, depending on the filing-system and OS policy (eg in the absence of quotas), may cause all your private data to be hidden reasonably effectively, though look out for swap files, etc.
Requires SecureRandom and should work with any Java2.
Creates a file called ZapDisc.TMP in the current directory and continually ties to extend it with blocks of random data until it cannot do so any further. This then comes back and overwrites the file with different random data several times, hopefully thoroughly hiding any sensitive data that used to be present, and then deletes the file to give the space back to the file system.
The name of the file, the size of block it writes, and and limit on the amount of space it tries to use, etc, may be settable in the future.
Works with JDK 1.2+. @author Damon Hart-Davis (http://d.hd.org/) */ public final class ZapDisc { /**Prefix of filename to fill with random noise to use up free space. */ public static final String filenamePrefix = "ZapDisc"; /**Extension of filename to fill with random noise to use up free space. */ public static final String filenameExt = ".TMP"; /**Number of overwrites to do with different random data. * 1 is minimum, 3 is reasonable, 5--10 is probably the maximum sensible. */ public static final int numOverwrites = 3; /**Our secure random number source. * This has to generate good enough random numbers to have no * obvious pattern that can easily be "removed" to get back at * the original, pre-overwrite, state if possible. */ private static final SecureRandom srnd = new SecureRandom(); /**Minimum size of block of random numbes to write, in blocks. * The larger this is the more efficient the disc writes will probably be, * at the cost of slightly more expense generating the random data, * and the need for more complicated algorithms to deal with any partial * blocks that we might write at the end of our temporary file. *
* Having a large prime-ish number of kbytes makes for a more interesting * disk layout patten of the random noise, and thus a potentially-better * hiding of the original data. */ public static final int MIN_DATA_BLOCKS = 53; /**Size of a putative efficient-to-write disc block. * Should be at least 1024 to cover common 512-byte hard-disc sectors * and 1024-byte optical media sector sizes. *
* Blocks of 8192 or larger should help UFS-like systems achieve * relatively high bandwidth. */ public static final int BLOCK_SIZE = 8192; /**Time to wait between printing progress dots, in ms. */ public static final int PROGRESS_MS = 15000; /**Main entry point. */ public static void main(String args[]) { System.out.println("Free-space overwrite using files " + filenamePrefix + "NNN" + filenameExt + "..."); // Number of temporary file, from 1 upwards. int fileNumber; for(fileNumber = 1; ; ++fileNumber) { final File f = new File(filenamePrefix + fileNumber + filenameExt); if(f.exists()) { System.err.println("File "+f+" already exists; will not continue."); System.err.println("Remove it and earlier files by hand if need be."); Runtime.getRuntime().exit(1); // Error... } System.out.println(" Free-space overwrite using file " + f + "."); System.out.println(" (Will remove when finished.)"); f.deleteOnExit(); // Register for automatic removal on exit. RandomAccessFile ras = null; try { ras = new RandomAccessFile(f, "rw"); for(int pass = 1; pass <= numOverwrites; ++pass) { System.out.println("****PASS " + pass + "..."); // Create our data buffer and random noise. // Vary the size quite a lot to make each overwrite pattern // different for more effective data hiding. final int dataBlockSize = BLOCK_SIZE * (1 | (MIN_DATA_BLOCKS + srnd.nextInt(7 * MIN_DATA_BLOCKS))); System.out.println(" (Creating random overwrite data pattern length "+dataBlockSize+" bytes...)"); final byte randomData[] = new byte[dataBlockSize]; // Create random data. srnd.nextBytes(randomData); // Seek to start of file. ras.seek(0); // Go on writing until we hit an error... // We start by writing the huge block at once, // but each time we encounter an error // we halve the length that we try to write // until we cannot even write a single byte per time // at which point we assume that we've consumed // all the disc space we can. System.out.print(" Writing data..."); long time = 0; for(int writeSize = randomData.length; writeSize > 0; writeSize /= 2) { try { for( ; ; ) { // Overwrite the disc space with random data... ras.write(randomData, 0, writeSize); final long now = System.currentTimeMillis(); if(now - time > PROGRESS_MS) { System.out.print('.'); time = now; } } } catch(IOException e) { // Visually note that we are // switching to a smaller block size... System.out.print('e'); time = 0; // Force "." if we write successfully... } } System.out.println(""); System.out.println("Cannot extend file..."); System.out.println("Stopped writing with " + "pos = " + ras.getFilePointer() + ", " + "length = " + ras.length() + "."); // If we couldn't actually write any data to the file // at all we assume that the disc is actually as full // as we can make it, so we don't attempt to recover. if((ras.getFilePointer() == 0) || (ras.length() == 0)) { throw new EOFException("could not write to file: disc assumed to be full"); } } } catch(IOException e) { System.err.println("Error in file " + f + ": " + e.getMessage()); break; // OK, assume that the disc is as full as we can make it. } finally { if(ras != null) try { ras.close(); } catch(IOException e) { } } } // for( ; fileNumber > 0; --fileNumber) // { // final File f = new File(filenamePrefix + fileNumber + filenameExt); // System.out.println("Removing file " + f + "."); // f.delete(); // } } }