大家把ntfs分区上的文件拷贝到非ntfs分区上时, 可能偶尔遇到过下面的情况, 系统提示会有数据丢失, 这是怎么回事呢?  实际上ntfs文件系统引入了"流"这个概念, 每个文件都可以有多个流, 而我们一般只使用了一个, 通过给文件分配更多的流, 可以实现某种意义上的"文件隐藏". 例如可以控制台中使用下面的命令建立一个文本文件: dir d:>abc.txt 它列出d:根目录的所有文件, 然后将其重定向到文件abc.txt, 现在你可以检查一下abc.txt的大小和内容, 并记录下来. 然后再执行下面这条命令 dir c:>abc.txt:stream.txt 执行完毕后, 检查abc.txt, 大小和内容都没有变化, 但其实abc.txt已经多了一个流stream.txt, 而重定向的内容就输出到了它里面, 不信使用下面的命令看一下(注意流的名字也要以.txt结尾, 否则notepad就找不到了): notepad abc.txt:stream.txt 这样我们就把一个文件隐藏了, dir命令看不见, 文件属性看不到, 资源管理器也看不到, 如果不知道流的名字, notepad也是无法访问的. 实际上, 流还可以不依赖于文件, 下面的命令也是合法的(先不要试, 否则可能会有点麻烦): dir e:>:stream.txt 这是把流绑到了文件夹上, 这种流就更隐蔽了. 一般情况下要想删除流只有将其宿主删除, 如果你执行了刚才的命令, 并且是在根文件夹上执行的, 如果你想删除它, 那就恭喜你要格盘了:). 不过通过写程序还是不难删除流的, 只要调用DeleteFile, 并提供流的名字就行了. 要想枚举一个文件中的所有流, 目前只能通过BackupRead来完成. 我写了一个小程序, 通过它可以枚举、删除、导入导出流中的数据, 下面的是它的代码(写的比较仓促, 可能还有一些bug, 不过主要功能都实现了, 它的名字叫nsvw, 即Ntfs Stream Viewer). #include <windows.h>
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#include <malloc.h>
#include <stddef.h>


enum RUN_MODE
  {
SHOW_USAGE = 0,
SHOW_STREAMS,
DELETE_STREAMS,
IMPORT_STREAM,
EXPORT_STREAM,
};


LPCWSTR g_szObj = NULL;
LPCWSTR g_szStrm = NULL;
LPCWSTR g_szFile = NULL;


int ParseArgs( int argc, LPWSTR* argv )
  {
if( argc == 1 || argc == 3 )
return SHOW_USAGE;

g_szObj = *(argv + 1);
if( argc == 2 )
return SHOW_STREAMS;

LPCWSTR act = *(argv + 2);
if( act[0] != L'-' && act[0] != L'/' )
return SHOW_USAGE;

if( act[1] == L'd' )
return DELETE_STREAMS;

if( argc == 4 || argc > 5 )
return SHOW_USAGE;

g_szStrm = *(argv + 3);
g_szFile = *(argv + 4);
if( act[1] == L'i' )
return IMPORT_STREAM;

if( act[1] == L'e' )
return EXPORT_STREAM;

return SHOW_USAGE;
}


int ShowUsage()
  {
wprintf( L"USAGE: "
L"nsvw file.a : view streams in file.a "
L"nsvw file.a -d s1 s2 : delete stream s1, s2 and from file.a "
L"nsvw file.a -i s1 file.b : copy the content of file.b to stream s1 in file.a "
L"nsvw file.a -e s1 file.c : copy the content of stream s1 in file.a to file.c "
);
return 0;
}


int ShowStreams()
  {
HANDLE hFile = CreateFile( g_szObj, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hFile == INVALID_HANDLE_VALUE )
 {
wprintf( L"Unable to open object "%s" ", g_szObj );
return static_cast<int>( GetLastError() );
}

 WIN32_STREAM_ID wsi = {0};
WCHAR szStrmName[MAX_PATH];
LPVOID pContext = NULL;

BOOL bOk = TRUE;
int nCount = 0;
while( bOk )
 {
DWORD dwBytes = 0;
LPBYTE buf = reinterpret_cast<LPBYTE>( &wsi );
DWORD dwSize = static_cast<DWORD>(offsetof(WIN32_STREAM_ID, cStreamName));
bOk = BackupRead( hFile, buf, dwSize, &dwBytes, FALSE, FALSE, &pContext );
if( !bOk || dwBytes == 0 )
break;
if( wsi.dwStreamNameSize > 0 )
 {
buf = reinterpret_cast<LPBYTE>( szStrmName );
dwSize = wsi.dwStreamNameSize;
BackupRead( hFile, buf, dwSize, &dwBytes, FALSE, FALSE, &pContext );
szStrmName[dwSize / sizeof(WCHAR)] = 0;
wprintf( L"NAME: "%s" SIZE: %I64d ", szStrmName, wsi.Size.QuadPart );
++nCount;
}
DWORD dw1, dw2;
BackupSeek( hFile, wsi.Size.LowPart, wsi.Size.HighPart, &dw1, &dw2, &pContext );
}

DWORD dwError = GetLastError();
::BackupRead( hFile, NULL, 0, NULL, TRUE, FALSE, &pContext );
::CloseHandle( hFile );

wprintf( L"Total %d stream(s). ", nCount );

return static_cast<int>( dwError );
}



void BuildStreamName( LPCWSTR szStrm, LPWSTR buf, size_t size )
  {
_snwprintf( buf, size, L"%s:%s", g_szObj, szStrm );
buf[size - 1] = 0;
}



int DeleteStreams( int count, LPWSTR* streams )
  {
const int nSize = MAX_PATH * 2;
WCHAR szStrmName[nSize];
size_t size = sizeof(szStrmName) / sizeof(WCHAR);

for( int i = 0; i < count; ++i )
 {
BuildStreamName( *(streams + i), szStrmName, nSize );
if( ::DeleteFileW( szStrmName ) )
wprintf( L"stream %s was deleted. ", *(streams + i) );
else
wprintf( L"unable to delete stream %s. ", *(streams + i) );
}

return 0;
}


int CopyStream( LPCWSTR szSrc, LPCWSTR szDst )
  {
int nRet = 0;
HANDLE hSrc = INVALID_HANDLE_VALUE, hDst = INVALID_HANDLE_VALUE;
HANDLE hSrcFm = NULL, hDstFm = NULL;
PVOID pSrc = NULL, pDst = NULL;

__try
 {
hSrc = ::CreateFile( szSrc, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if( hSrc == INVALID_HANDLE_VALUE )
__leave;

DWORD dwSize = ::GetFileSize( hSrc, NULL );
if( dwSize > 0 )
 {
hSrcFm = ::CreateFileMapping( hSrc, NULL, PAGE_READONLY, 0, 0, NULL );
if( hSrcFm == NULL )
__leave;

pSrc = ::MapViewOfFile( hSrcFm, FILE_MAP_READ, 0, 0, dwSize );
if( pSrc == NULL )
__leave;
}

hDst = ::CreateFile( szDst, FILE_ALL_ACCESS, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if( hDst == INVALID_HANDLE_VALUE )
__leave;

if( dwSize > 0 )
 {
hDstFm = ::CreateFileMapping( hDst, NULL, PAGE_READWRITE, 0, dwSize, NULL );
if( hDstFm == NULL )
__leave;

pDst = ::MapViewOfFile( hDstFm, FILE_MAP_WRITE, 0, 0, dwSize );
if( pDst == NULL )
__leave;

memcpy( pDst, pSrc, dwSize );
}
else
 {
::SetFilePointer( hDst, 0, 0, FILE_BEGIN );
::SetEndOfFile( hDst );
}
}
__finally
 {
nRet = static_cast<int>( ::GetLastError() );
}

if( pDst != NULL )
::UnmapViewOfFile( pDst );
if( pSrc != NULL )
::UnmapViewOfFile( pSrc );
if( hDstFm != NULL )
::CloseHandle( hDstFm );
if( hSrcFm != NULL )
::CloseHandle( hSrcFm );
if( hDst != INVALID_HANDLE_VALUE )
::CloseHandle( hDst );
if( hSrc != INVALID_HANDLE_VALUE )
::CloseHandle( hSrc );

return nRet;
}


int ImportStream()
  {
const int nSize = MAX_PATH * 2;
WCHAR szStrmName[nSize];
size_t size = sizeof(szStrmName) / sizeof(WCHAR);
BuildStreamName( g_szStrm, szStrmName, nSize );
int nRes = CopyStream( g_szFile, szStrmName );
if( nRes != 0 )
wprintf( L"Import failed. " );
else
wprintf( L"Import completed. " );
return nRes;
}


int ExportStream()
  {
const int nSize = MAX_PATH * 2;
WCHAR szStrmName[nSize];
size_t size = sizeof(szStrmName) / sizeof(WCHAR);
BuildStreamName( g_szStrm, szStrmName, nSize );
int nRes = CopyStream( szStrmName, g_szFile );
if( nRes != 0 )
wprintf( L"Export failed. " );
else
wprintf( L"Export completed. " );
return nRes;

}



int __cdecl wmain( int argc, LPWSTR* argv )
  {
int nRetCode = 0;

_wsetlocale( LC_ALL, L".OCP" );
wprintf( L"NTFS Stream Viewer VERSION 1.0 " );

switch( ParseArgs( argc, argv ) )
 {
case SHOW_USAGE:
nRetCode = ShowUsage();
break;

case SHOW_STREAMS:
nRetCode = ShowStreams();
break;

case DELETE_STREAMS:
nRetCode = DeleteStreams( argc - 3, argv + 3 );
break;

case IMPORT_STREAM:
nRetCode = ImportStream();
break;

case EXPORT_STREAM:
nRetCode = ExportStream();
break;

default:
wprintf( L"internel error! " );
nRetCode = -1;
break;
}

return nRetCode;
}

|