大家把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;
}