Click here to Skip to main content
Click here to Skip to main content
Go to top

Accessing alternative data-streams of files on an NTFS volume

, 28 Jul 2010
Rate this:
Please Sign up or sign in to vote.
A pair of classes to encapsulate access to NTFS alternative data streams.

Introduction

Since NT 3.1, the NTFS file system has supported multiple data-streams for files. There has never been built-in support for viewing or manipulating these additional streams, but the Windows API functions include support for them with a special file syntax: Filename.ext:StreamName. Even Win9x machines can access the alternative data streams of files on any NTFS volume they have access to, e.g., through a mapped drive. Because the Scripting.FileSystemObject and many other libraries call the CreateFile API behind the scenes, even scripts have been able to access alternative streams quite easily (although enumerating the existing streams has always been tricky).

In .NET, however, it seems someone decided to add some checking to the format of filenames. If you attempt to open a FileStream on an alternative stream, you will get a "Path Format not supported" exception. I have been unable to find any class in the CLR that provides support for alternative data streams, so I decided to roll my own.

Update

I originally wrote this code six years ago, targeting v1 of the .NET Framework. Looking at the code now, it seems quite messy, and has several bugs and problems which were mentioned in the comments. I have since completely re-written the code for .NET v3.5, and (hopefully) fixed the bugs.

The new code is not compatible with the original version. However, I have included a sample compatibility wrapper which maps the old API to the new API. You can find these files under the "other/Compatibility wrapper" folder in the download.

Bugs / Issues Fixed

  • The code now uses the FileSystemInfo class rather than the FileInfo class. This allows you to access alternate data streams attached to folders as well as files. (Suggested by Dan Elebash.)
  • The code now uses SafeFileHandle instead of IntPtr for the file handle. (Suggested by Moomansun.)
  • The code is now 64-bit compatible. (Reported by John SMith 5634552745.)
  • The code now correctly calls BackupRead with the bAbort parameter set to true after enumerating the streams. (Reported by scooter_jsm.)
  • The number of global memory allocations required to read the streams from a file has been reduced. (Suggested by scooter_jsm.)
  • v2.1: ValidateStreamName now accepts stream names which contain characters with ASCII codes between 1 and 31. (Reported by Andy Missico.)
  • v2.1: The code will now attempt to map standard IO errors to the equivalent .NET Exception type. (Suggested by Andy Missico.)

Using the Classes

The AlternateDataStreamInfo class represents the details of an individual stream, and provides methods to create, open, or delete the stream.

The static FileSystem class provides methods to retrieve the list of streams for a file, retrieve a specific stream from a file, determine whether a stream exists, and delete a specific stream.

All methods on the FileSystem class offer overloads which accept either a path or a FileSystemInfo object. The overloads which accept a FileSystemInfo object can also be invoked as extension methods.

Example:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Trinet.Core.IO.Ntfs;

...

FileInfo file = new FileInfo(path);

// List the additional streams for a file:
foreach (AlternateDataStreamInfo s in file.ListAlternateDataStreams())
{
    Console.WriteLine("{0} - {1} bytes", s.Name, s.Size);
}

// Read the "Zone.Identifier" stream, if it exists:
if (file.AlternateDataStreamExists("Zone.Identifier"))
{
    Console.WriteLine("Found zone identifier stream:");
    
    AlternateDataStreamInfo s = 
       file.GetAlternateDataStream("Zone.Identifier",
                                   FileMode.Open);
    using (TextReader reader = s.OpenText())
    {
        Console.WriteLine(reader.ReadToEnd());
    }
    
    // Delete the stream:
    s.Delete();
}
else
{
    Console.WriteLine("No zone identifier stream found.");
}

// Alternative method to delete the stream:
file.DeleteAlternateDataStream("Zone.Identifier");

Files Included

  • The Trinet.Core.IO.Ntfs folder contains the source code.
  • The doc folder contains the documentation and FxCop project.
  • The bin folder contains the compiled assembly.
  • The other folder contains the compatibility wrapper, and a sample to recursively delete the "Zone.Identifier" stream from all files in a given path.

References

If you want more information on NTFS programming, or the C++ code I based this on, see Dino Esposito's MSDN article from March 2000: http://msdn.microsoft.com/en-us/library/ms810604.aspx [^].

History

  • v1 - 1 August 2002 - Initial release, targeting .NET 1.0.
  • v2 - 16 September 2008 - Re-written to target .NET 3.5; major changes include:
    • Replaced FileInfo with FileSystemInfo.
    • Replaced IntPtr with SafeFileHandle.
    • Made the P/Invoke calls 64-bit compatible.
    • Made the code abort the BackupRead operation after enumerating the streams.
    • Reduced the number of global memory allocations required.
  • v2.1 - 28 July 2010 - Minor fixes suggested by Andy Missico:
    • Changed ValidateStreamName to accept stream names containing characters with ASCII codes between 1 and 31 (see msdn.microsoft.com/en-us/library/aa365247).
    • Added mapping of standard IO errors to the correct .NET exceptions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Richard Deeming
Software Developer Nevalee Business Solutions
United Kingdom United Kingdom
No Biography provided
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralMy vote of 5 PinmemberKeith L Robertson6-Sep-13 7:26 
SuggestionNotSupportedException for non-NTFS PinmemberKeith L Robertson6-Sep-13 7:32 
GeneralRe: NotSupportedException for non-NTFS PinprofessionalRichard Deeming9-Sep-13 1:31 
QuestionNot showing all data Pinmemberledtech33-Jan-13 9:29 
GeneralWow :) PinmemberVitaly Tomilov25-Jun-12 10:47 
QuestionSerialization PinmemberJohn Vonesh20-Apr-12 7:22 
AnswerRe: Serialization PinmemberRichard Deeming20-Apr-12 7:37 
GeneralRe: Serialization PinmemberJohn Vonesh20-Apr-12 9:02 
GeneralRe: Serialization [modified] PinmemberJohn Vonesh20-Apr-12 9:57 
Questionerror PinmemberPrince iraq16-Mar-12 0:11 
AnswerRe: error PinmemberRichard Deeming16-Mar-12 3:04 
GeneralRe: error PinmemberPrince iraq16-Mar-12 7:40 
Question2Tb Virtual Disk Pinmembernhchmg27-Jun-11 20:16 
AnswerRe: 2Tb Virtual Disk PinmemberRichard Deeming5-Jul-11 7:38 
QuestionHow to close the stream? PinmemberDOSSTONED22-Feb-11 4:26 
AnswerRe: How to close the stream? PinmemberRichard Deeming22-Feb-11 6:15 
GeneralRe: How to close the stream? PinmemberDOSSTONED22-Feb-11 16:35 
GeneralConvenience methods PinmemberVenDiddy6-Aug-10 8:22 
GeneralExample code still not running properly on mapped network drive [modified] PinmemberLastZolex14-Jun-10 11:29 
Hi there.
 
Maybe I don't get it right, but I can't get the console app "TestNTFS" from your folder "\other\Compatibility wrapper" running properly.
 
I always get the exception (it's a german one)
 
System.ComponentModel.Win32Exception was unhandled
  Message=Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch
  Source=Trinet.Core.IO.Ntfs
  ErrorCode=-2147467259
  NativeErrorCode=123
  StackTrace:
       at Trinet.Core.IO.Ntfs.SafeNativeMethods.ThrowLastWin32Error() in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\SafeNativeMethods.cs:line 182
       at Trinet.Core.IO.Ntfs.SafeNativeMethods.SafeGetFileAttributes(String name) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\SafeNativeMethods.cs:line 222
       at Trinet.Core.IO.Ntfs.FileSystem.GetAlternateDataStream(FileSystemInfo file, String streamName, FileMode mode) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs\FileSystem.cs:line 231
       at NTFS.FileStreams.Add(String streamName) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\FileStreams.cs:line 138
       at NTFS.Test.TestNTFS.TestStream() in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\TestNTFS.cs:line 52
       at NTFS.Test.TestNTFS.Main(String[] args) in N:\CSharp\MemoryClones\MeMoMeSharply\trunk\Trinet.Core.IO.Ntfs.Tests\TestNTFS.cs:line 17
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
 
The above exception says: "The filename, directory name, or volume label syntax is incorrect", and I have no idea why this is so.
The testsystem is XP32 SP2 with Visual Studio 2010 (and because of VC2010 it is of course .NET 4.0).
 
As it was mentioned some postings below I have to admit there is something special about the drive "N:" I use. It is a mapped network drive and the VS2010 is running in an virtualized environment (VMWare).
 
The mapped network drive IS causing my troubles! I tried the demo app in a path "C:\\tmp" and it worked.
Some postings below there was someone with a similar problem, but as the comment says "mapped network drives are working properly" I hoped it would work out. But it does not.
 
Does anybody know a clean solution for this problem, if the app does not know that the drive is a mapped network drive?
 
Any help is appreciated!
 
Thanks in advance!
 
LZ
 
UPDATE #1:
 
Ok, I annoy you now with my progress in solving the problem. It is unsolved yet Wink | ;-)
 
As a first try I added a "networkdrive to UNC path" method which i borrowed here: GetUNCPath-Method
 
This transforms my original mapped network path from
 
"N:\\CSharp\\MemoryClones\\MeMoMeSharply\\trunk\\Trinet.Core.IO.Ntfs.Tests\\bin\\Debug\\test.jpg:thumb:$DATA"
 
to
 
"\\\\vmware-host\\Shared Folders\\Projekte\\CSharp\\MemoryClones\\MeMoMeSharply\\trunk\\Trinet.Core.IO.Ntfs.Tests\\bin\\Debug\\test.jpg:thumb:$DATA"
 
But this changed nothing. It still works on the local HDD path but not on the remote network path.
 
So I still need help from you, if possible Wink | ;-)

modified on Tuesday, June 15, 2010 2:43 AM

GeneralRe: Example code still not running properly on mapped network drive PinmemberRichard Deeming15-Jun-10 1:38 
GeneralRe: Example code still not running properly on mapped network drive [modified] PinmemberLastZolex15-Jun-10 2:09 
GeneralRe: Example code still not running properly on mapped network drive [modified] PinmemberAshutosh Bhawasinka12-Mar-12 16:51 
GeneralThis article is a great help! PinmemberLastZolex11-Jun-10 23:44 
GeneralNeed to Map Win32Exception to Appropriate .NET Exception PinmemberAndy Missico30-Mar-10 13:07 
GeneralRe: Need to Map Win32Exception to Appropriate .NET Exception PinmemberRichard Deeming27-Jul-10 7:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 28 Jul 2010
Article Copyright 2002 by Richard Deeming
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid