How to Read and Modify Ntfs Alternate Data Streams Using .Net

How to read and modify NTFS Alternate Data Streams using .NET

Not in .NET:

http://support.microsoft.com/kb/105763

#include <windows.h>
#include <stdio.h>

void main( )
{
HANDLE hFile, hStream;
DWORD dwRet;

hFile = CreateFile( "testfile",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL );
if( hFile == INVALID_HANDLE_VALUE )
printf( "Cannot open testfile\n" );
else
WriteFile( hFile, "This is testfile", 16, &dwRet, NULL );

hStream = CreateFile( "testfile:stream",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL );
if( hStream == INVALID_HANDLE_VALUE )
printf( "Cannot open testfile:stream\n" );
else
WriteFile(hStream, "This is testfile:stream", 23, &dwRet, NULL);
}

Working C# Example: Writing & Reading NTFS Alternate Data Stream Under Win7 64 bit

I've had success on Windows 7 x64 using this library:

https://github.com/hubkey/Trinet.Core.IO.Ntfs

I can't find my old code, and the documentation page I have bookmarked is down at the moment, so I'll try to post some code when it's back up if you're still having problems.

Edit:
Apparently it's as simple as:

using Trinet.Core.IO.Ntfs;

var fileInfo = new FileInfo(@"C:\path\to\file.dat");
if (AlternateDataStreamExists("MyStreamName"))
{
var alternateStream = fileInfo.GetAlternateDataStream("MyStreamName").OpenRead();
}
else
{
var alternateStream = fileInfo.GetAlternateDataStream("MyStreamName").OpenWrite();
}

Accessing alternate data streams in files

Unfortunately no, there is still no access via a managed .Net Framework API.

Update

Here's a library\source for an ADS access wrapper:

http://www.codeproject.com/KB/cs/ntfsstreams.aspx

How do I read Windows NTFS's Alternate Data Stream using Java's IO?

I was able to read the ADS of a file simply by opening the the file with the syntax "file_name:stream_name". So if you've done this:

C:>echo Hidden text > test.txt:hidden

Then you should be able to do this:

package net.snortum.play;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class AdsPlay {
public static void main(String[] args) {
new AdsPlay().start();
}

private void start() {
File file = new File("test.txt:hidden");
try (BufferedReader bf = new BufferedReader( new FileReader(file))) {
String hidden = bf.readLine();
System.out.println(hidden);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

If you want to get the ADS data from the dir /r command, I think you just need to execute a shell and capture the output:

package net.snortum.play;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExecPlay {

public static void main(String[] args) {
new ExecPlay().start();
}

private void start() {
String fileName = "not found";
String ads = "not found";
final String command = "cmd.exe /c dir /r"; // listing of current directory

final Pattern pattern = Pattern.compile(
"\\s*" // any amount of whitespace
+ "[0123456789,]+\\s*" // digits (with possible comma), whitespace
+ "([^:]+):" // group 1 = file name, then colon
+ "([^:]+):" // group 2 = ADS, then colon
+ ".+"); // everything else

try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;

while ((line = br.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
fileName = matcher.group(1);
ads = matcher.group(2);
break;
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(fileName + ", " + ads);

}
}

Now you can use the first code listing to print the ADS data.

How to add a custom property in a file in .Net?

You could consider using ADS (alternate data streams) which is an NTFS feature. ADS allow you to attach content to files on NTFS file systems. From the point-of-view of the user, there is a single file, but this file may have additional streams attached to it which will not be visible from Explorer for example. When the file is copied directly between NTFS file systems, the streams get copied too.

Alternate data streams are used by the OS to tag files with information such as what zone a file arrived from (e.g. the internet) and this drives security warning dialogs in such cases.

An example of using streams from C# can be found here:

NTFS Alternate Data Streams - .NET

Pros:

  • No loose files containing your extra meta-data.
  • When files are copied on NTFS file systems, streams are automatically copied.

Cons:

  • Only works on NTFS e.g. not FAT32.
  • If a file is copied onto another file system format, streams are lost.
  • If a file is packaged via some other fornat (e.g. as an email attachment) the streams may be lost.

reading alternative streams results into NullReferenceException

CreateFileW does not return a pointer to some memory location, it returns a handle. To work with that, you need FileStream, not UnmanagedMemoryStream:

using (var stream = new FileStream(mainStream, FileAccess.Read))

Though this overload of the constructor is obsolete, you should use SafeFileHandle instead of IntPtr (and make sure to Dispose it):

[CLSCompliant(false)]
[DllImport("kernel32.dll", EntryPoint = "CreateFileW")]
public static extern SafeFileHandle CreateFileW(
[In] [MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
[In] IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
[In] IntPtr hTemplateFile
);

Also, I don't know anything about Zone.Identifier, but for me the stream doesn't contain a single character, like you seem to be expecting, it contains:

[ZoneTransfer]
ZoneId=3

To get to that, you can wrap the FileStream in a StreamReader. So, the whole code to get the above string is:

using (var streamHandle = NativeMethods.CreateFileW(
@"<path to batch file directory>any.bat:Zone.Identifier",
NativeConstants.GENERIC_READ, NativeConstants.FILE_SHARE_READ,
IntPtr.Zero, NativeConstants.OPEN_EXISTING, 0, IntPtr.Zero))
using (var stream = new FileStream(streamHandle, FileAccess.Read))
using (var reader = new StreamReader(stream))
{
Console.WriteLine(reader.ReadToEnd());
}


Related Topics



Leave a reply



Submit