Sunday, October 7, 2007

Hide & Seek: Using NTFS Alternate Data Streams

The NTFS file system utilized by Windows (NT and later) includes support for Alternate Data Streams (ADS). ADS was implemented in NTFS to provide compatibility with Apple's Macintosh Hierarchical File System (HFS), which uses resource forks to store icons and other information for a file, but such streams can be used to store any type of data that a normal file would store. You could, for example, use an ADS to store metadata about the file to which the ADS is attached. You could even store binary data inside an ADS. This might be useful for storing backup versions of a file before making changes.

To work with an alternate data stream for a file, simply append a colon and the stream name to the filename. The following code appends a stream named 'stream1' to file.txt:


open('file.txt:stream1', 'w') do |f|
f.puts('Your Text Here')
end

If file.txt did not exist, it would have been created and would be 0 bytes in size, since no data was written to the main stream. But you can append streams to an existing file, as well. The reported size of the file would not change, though it now includes alternate data streams of embedded data.

Reading from a stream is just as simple:

stream_text = open('file.txt:stream1').read

Alternate data streams can also be used for storing binary data, including executables:

bytes = open('MyApp.exe', 'rb').read
open('file.txt:MyApp.exe', 'wb') do |f|
f.write(bytes)
end

bytes = open('file.txt:MyApp.exe','rb').read
open('MyApp2.exe', 'wb') do |f|
f.write(data)
end

A file can contain multiple data streams, so you could, for example, include a data stream for text and another stream for binary data.

Another possible use for ADS would be to create a backup of the file which could be restored later:

def create_backup(filename)
open("#{filename}:backup", "wb") do |f|
f.write(open(filename, "rb").read)
end
end

def restore_backup(filename)
if not File.exist?("#{filename}:backup")
puts("Backup stream does not exist!")
return
end
backup = open("#{filename}:backup", "rb").read
open("#{filename}", "wb") do |f|
f.write(backup)
end
end

Note: A file's alternate data streams will be preserved on the NTFS disk, but would be stripped off of a file when copied to a non-NTFS disk, such as a flash drive or CD/DVD disk; or when a file is copied via ftp. For this reason you may not want to rely on alternate data streams for storing critical data.

There you have it. Sound interesting? Further details on Alternate Data Streams can be found here and here.

Thanks for stopping by!

Digg my article

1 comment:

Arnaud M said...

Nice digging this up, amazing !