2011-07-23 14 views
1

私は増分バックアップシステムで作業しようとしていますが、スクリプトを実行すると、 "違反のためファイルを開くことができません"インクリメンタルバックアップを実行するために開くために、すでに別のプロセスによって使用されています。 私はこのプロセスをやめて、ファイルを解放してインクリメンタルバックアップを行うことができますが、それは本当に避けなければならないことです。 私はWin32 APIを使ってファイルハンドラを複製することができますので、どうすればいいですか?ここでファイルの重複が発生して共有違反が発生しないようにするエラーメッセージ

は、私のコードの一部です:

FILE *GetFileHandle(WIN32_FIND_DATA *pWfdStruct, BOOL bWrite){ 

FILE *fFile; 
DWORD nGLE; 

fFile = fopen(pWfdStruct->cFileName, "rb"); 
if (!fFile) 
{ 
    nGLE = GetLastError(); 

    if (nGLE == ERROR_SHARING_VIOLATION) // 32 
    { 
     char szCurDir[8192]; 

     GetCurrentDirectory(8192, szCurDir); 
     ODS("WARN: cannot open %s file due to sharing violation [fRenameFile: %s\\%s]\n", 
      bWrite ? "dst" : "src", szCurDir, pWfdStruct->cFileName); 
     return 0; 
    } 

    if (nGLE == ERROR_ACCESS_DENIED)  // 5 
    { 
     char szCurDir[8192]; 

     GetCurrentDirectory(8192, szCurDir); 
     ODS("WARN: cannot open %s file, access denied [fRenameFile: %s\\%s]\n", 
      bWrite ? "dst" : "src", szCurDir, pWfdStruct->cFileName); 
     return 0; 
    } 

    if (nGLE == ERROR_FILE_NOT_FOUND)  // 2 
    { 
     char szCurDir[8192]; 

     GetCurrentDirectory(8192, szCurDir); 
     ODS("WARN: cannot open %s file, file not present [fRenameFile: %s\\%s]\n", 
      bWrite ? "dst" : "src", szCurDir, pWfdStruct->cFileName); 
     return 0; 
    } 

    char szCurDir[8192]; 
    GetCurrentDirectory(8192, szCurDir); 

    if (bWrite) 
    { 
     ODS("WARN: cannot open dst file [fRenameFile: %s\\%s] [GLE: %d]\n", 
      szCurDir, pWfdStruct->cFileName, nGLE); 
     return 0; 
    } 

    ODS("WARN: cannot open src file [fRenameFile: %s\\%s] [GLE: %d] trying alt name [%s]\n", 
     szCurDir, pWfdStruct->cFileName, nGLE, pWfdStruct->cAlternateFileName); 
    ReportSystemError("GetFileHandle", nGLE); 

    __try 
    { 
     if (pWfdStruct->cAlternateFileName[0]) 
     { 
      fFile = fopen(pWfdStruct->cAlternateFileName, "rb"); 
     } 
    } 
    __except(EXCEPTION_EXECUTE_HANDLER) 
    { 
     ODS("Exception caught\n"); // give up 
    } 

    if (!fFile) 
    { 
     nGLE = GetLastError(); 
     ReportSystemError("GetFileHandle 2nd try", nGLE); 
     FATALODS("FATAL error, cannot open src file [%s] [GLE: %d]", pWfdStruct->cFileName, nGLE); 
    } 
    else 
    { 
     ODS("File: %s open success\n", pWfdStruct->cAlternateFileName); 
    } 
} 
return fFile;} // GetFileHandle 

あなたは私を助けていただけますか?

答えて

0

ファイルが別のプロセスによってロックされている場合は、そのファイルを開くことはできません。期間。

Windowsには、特にバックアップクライアントを構築する目的で、Volume Shadow Copyサービス(VSS)があります。ロックされたファイルをコピーし、一貫性のあるスナップショットなどを確保するための準備をしています。APIは少し畳み込まれていますが、堅牢なバックアップソリューションが必要な場合は他にありません。

VSSを使用する例については、オープンソースのHoboCopyバックアップツールを参照してください。

EDIT:いくつかのグーグルの後、私はあなたが望むことをする2つの例を見つけました。私はこれらの仕事を保証することはできませんが、少なくとも彼らは妥当と思われます。

$include "windowssdk.inc" 
$include "ntddk.inc" 
$include "undocumented.inc" 
$include "stdio.inc" 
$use "_crtdll.lib" 


'-------------------------- example 
$include "shlwapi.inc" 
$define REPLACE_ALWAYS 
' open youtube in internet explorer, watch any video (should be downloaded in 100%) 
' then run this program to copy the locked fla***.tmp file from your TEMP directory 
istring tmppath[MAX_PATH] 

' 1. enumerate fla*.tmp files in TEMP directory 
ExpandEnvironmentStrings("%TEMP%\\", tmppath, MAX_PATH) 
int pathlen = len(tmppath) 
strcpy(&tmppath[pathlen], "fla*.tmp") 
UINT hFind = findopen(tmppath) 
int nFilesFound = 0 
int nFilesCopied = 0 

if (hFind) 
    istring name[256] = findnext(hFind) 
    while (name[0] <> 0) 

     strncpy(&tmppath[pathlen], name, MAX_PATH-pathlen) 
     ' tmppath = local path to flash video 

     ' 2. copy the file with renamed extension (supported by media player classic) 
     istring newpath[MAX_PATH] 
     newpath = tmppath 
     PathRenameExtension(newpath, ".flv") 

     ' check if we can open the file directly 
     HANDLE hFile = CreateFile(tmppath, GENERIC_READ, FILE_SHARE_READ _ 
      | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0) 

     if (hFile <> INVALID_HANDLE_VALUE) 
      ' file opened, so you can copy it with your favorite file manager 
      CloseHandle(hFile) 

     else 

      nFilesFound++ 
      ' the file is opened with exclusive access, call the subroutine below to open it 
      HANDLE hProcess 
      hFile = OpenFileEx(tmppath, &hProcess) 
      if (hFile = INVALID_HANDLE_VALUE) 
       ' failed 
       MessageBox 0, "failed to open " + tmppath, "" 
      else 
       ' copy it now ... 
$ifdef REPLACE_ALWAYS 
       HANDLE hOutFile = CreateFile(newpath, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0) 
$else 
       HANDLE hOutFile = CreateFile(newpath, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0) 
$endif 
       if (hOutFile = INVALID_HANDLE_VALUE) 
        ' failed ? 
        MessageBox 0, "failed to create " + newpath, "" 
       else 
        ' ... but first suspend the owner process (because the handle is duplicated, and 
        ' if the owner process changes file pointer, it will be reflected also in this process) 
        declare import, NtSuspendProcess(HANDLE hProcess), NTSTATUS 
        declare import, NtResumeProcess(HANDLE hProcess), NTSTATUS 
        NtSuspendProcess(hProcess) 

        ' save original file pointer in originalFilePos variable 
        LARGE_INTEGER originalFilePos 
        LARGE_INTEGER nullFilePos 
        nullFilePos.QuadPart = 0q 
        SetFilePointerEx(hFile, nullFilePos, &originalFilePos, FILE_CURRENT) 

        ' seek to beginning 
        SetFilePointerEx(hFile, nullFilePos, NULL, FILE_BEGIN) 

        ' copy, using string name as the buffer 
        DWORD BytesRead, BytesWriten 
        while (ReadFile(hFile, &name, 256, &BytesRead, 0) and BytesRead) 
         WriteFile(hOutFile, name, BytesRead, &BytesWriten, 0) 
        endwhile 
        ' 
        nFilesCopied++ 
        ' restore file pointer 
        SetFilePointerEx(hFile, originalFilePos, NULL, FILE_BEGIN) 
        ' cleanup 
        CloseHandle(hOutFile) 
        ' resume the process 
        NtResumeProcess(hProcess) 
       endif 
       CloseHandle(hFile) 
      endif 
     endif 
     name = findnext(hFind) 
    endwhile 

    findclose(hFind) 
endif 

if (MessageBox(0, using("Copied # from # locked .FLV files. Open Directory ?", nFilesCopied, nFilesFound), "", MB_YESNO) = IDYES) 
    tmppath[pathlen-1] = 0 
    system tmppath 
endif 

'-------------------------- code 

type SYSTEM_HANDLE_ENTRY 
    ULONG OwnerPid 
    BYTE ObjectType 
    BYTE HandleFlags 
    USHORT HandleValue 
    PVOID ObjectPointer 
    ACCESS_MASK GrantedAccess 
endtype 

type SYSTEM_HANDLE_INFORMATION 
    ULONG HandleCount 
    SYSTEM_HANDLE_ENTRY Handles[1] 
endtype 

type MY_OBJECT_TYPE_INFORMATION 
    OBJECT_TYPE_INFORMATION t 
    iwstring buffer[64] 
endtype 

type MY_OBJECT_NAME_INFORMATION 
    OBJECT_NAME_INFORMATION t 
    iwstring buffer[280] 
endtype 

declare import, NtQuerySystemInformation(_ 
    int SystemInformationClass,_ 
    PVOID SystemInformation,_ 
    ULONG SystemInformationLength,_ 
    pointer ReturnLength),NTSTATUS 

const SystemHandleInformation = 16 




sub OpenFileEx(string path, pointer pphProcess),HANDLE 
    MY_OBJECT_TYPE_INFORMATION htype 
    MY_OBJECT_NAME_INFORMATION name 

    *<HANDLE>pphProcess = 0 
    HANDLE h = INVALID_HANDLE_VALUE 

    ' convert c:\ to \Device\HardDiskVolume... 
    ' 1. extract partition letter 
    iwstring root[4] 
    root[0] = path[0], 58, 0 
    ' 2. convert it to \Device\HardDiskVolumeX 
    iwstring wszNTPath[280] 
    int cch = QueryDosDeviceW(root, wszNTPath, 280) 
    if (!cch) then return INVALID_HANDLE_VALUE 
    ' 3. append remaining folders and file name from string path parameter 
    ' so <path> "c:\Program Files" gets converted to <wszNTPath> "\Device\HardDiskVolume1\Program Files" 
    cch = wcslen(wszNTPath) 
    _snwprintf(&wszNTPath[cch], 280-cch, L"%S", &path[2]) 
    ' now get the list of all handles, and find the handle which name is equal to wszNTPath 

    ULONG BytesNeeded, BufferSize = 4096 
    pointer handles = new(char, BufferSize) ' SYSTEM_HANDLE_INFORMATION* 

    while (handles) 

     ' get the list of all user-mode handles 
     NTSTATUS status = NtQuerySystemInformation(SystemHandleInformation, handles, BufferSize, &BytesNeeded) 
     if (status = STATUS_INFO_LENGTH_MISMATCH) 

      ' BytesNeeded is not adjusted, so we need to increase buffer size 
      delete handles 
      BufferSize += 32768 
      handles = new(char, BufferSize) 

     elseif (!status) 

      settype handles, SYSTEM_HANDLE_INFORMATION 
      ' sort handles by owning process id 
      qsort(&*handles.Handles, *handles.HandleCount, len(SYSTEM_HANDLE_ENTRY), &SortHandlesCb) 

      pointer node = *handles.Handles 
      settype node, SYSTEM_HANDLE_ENTRY 
      ULONG OwnerPid = 0 
      HANDLE hProcess = 0 
      HANDLE hThisProcess = GetCurrentProcess() 
      BYTE ObjectTypeFile = 0 

      while (*handles.HandleCount) 

       *handles.HandleCount-- 

       if (*node.GrantedAccess & FILE_READ_DATA) ' 13019F 

        if (OwnerPid <> *node.OwnerPid) 

         OwnerPid = *node.OwnerPid 
         if (hProcess) then CloseHandle(hProcess) 
         hProcess = OpenProcess(PROCESS_DUP_HANDLE|PROCESS_SUSPEND_RESUME, FALSE, OwnerPid) 

        endif 
        if (hProcess) 

         HANDLE hObject 
         if (DuplicateHandle(hProcess, *node.HandleValue, hThisProcess, &hObject, 0, FALSE, DUPLICATE_SAME_ACCESS)) 

          if (GetFileType(hObject) = FILE_TYPE_DISK) 

           if (!ObjectTypeFile) ' query object type name as "integer" 

            if (!NtQueryObject(hObject, ObjectTypeInformation, &htype, len(htype), &BytesNeeded)) 

             if (!_wcsnicmp(htype.t.TypeName.Buffer, L"File", 4)) 
              ObjectTypeFile = *node.ObjectType 
             endif 
            endif 
           endif 

           ' do not query object name with granted access 0x0012019f (deadloock) 
           if (ObjectTypeFile and (ObjectTypeFile = *node.ObjectType) and (*node.GrantedAccess <> 0x0012019f)) 

            ' query file name 
            if (!NtQueryObject(hObject, ObjectNameInformation, &name, len(name), &BytesNeeded)) 

             if (name.t.Name.Buffer and !wcsicmp(name.t.Name.Buffer, wszNTPath)) ' compare 

              *<HANDLE>pphProcess = hProcess 
              delete handles 
              return hObject 
             endif 
            endif 
           endif 
          endif 
          CloseHandle(hObject) 
         endif 
        endif 
       endif 
       node = &*node[1] 
      endwhile 
      if (hProcess) then CloseHandle(hProcess) 
      delete handles 

     else 
      ' NtQuerySystemInformation failed 
      delete handles 
     endif 

    endwhile 

    if (handles) then delete handles 
    return h 
endsub 


declare cdecl SortHandlesCb(SYSTEM_HANDLE_ENTRY p1, SYSTEM_HANDLE_ENTRY p2),int 

sub SortHandlesCb(SYSTEM_HANDLE_ENTRY p1, SYSTEM_HANDLE_ENTRY p2),int 
    if (p1.OwnerPid = p2.OwnerPid) then return 0 
    if (p1.OwnerPid > p2.OwnerPid) then return 1 
    return -1 
endsub 

別の厄介な例はhttp://forum.sysinternals.com/topic7974.htmlである:

最初の例では、はっきりとよくコメント(言語は、いくつかの基本の方言が、非常に読みやすいです)。

両方の例では、ネイティブAPI関数とそのドキュメント化されていない機能を使用しています。

基本的な手順は以下のとおりです。

  1. ロック過程で見られるようなファイルハンドルの値を取得します。これは、NtQuerySystemInformationを呼び出してシステム内のすべてのハンドルのリストを取得し、NtQueryObjectを呼び出して、ファイル名が一致するハンドルを見つけます。 ファイル名はNTデバイス形式に変換する必要があります。
  2. オープンsuffucuent権限(PROCESS_DUP_HANDLEPROCESS_SUSPEND_RESUME)と呼んでDuiplicateHandle(ソース、ステップ1からハンドル値、および先としてあなたのプロセスとして、そのプロセスを使用)とハンドル(OpenProcess)を所有しているプロセス。
  3. NtSuspendProcessを使用してプロセスを「一時停止」し、ファイルポインタとファイルの内容を変更できないようにします)。
  4. 複製ハンドルを使用してファイルをコピーします。
  5. ファイルポインタを復元し、ハンドルを閉じ、プロセスをポストするにはNtResumeProcessを呼び出します。

EDIT2:個人的に私はロックされたファイルにアクセスするために別の方法を使用していた - マニュアルNTFSを解析すると、生のディスクアクセスを。例えば。ファイル名にMFTエントリが見つかると、データの実行場所をデコードし、それらをrawディスクから読み込みます。rawディスクへのアクセスは常に(管理者特権を持っていれば)利用可能であるため、どのファイルも読み込み可能です。欠点は一貫性保証がゼロであるため、バックアップ目的には適していません。

PS。もし私があなただったら私はまだ公式にサポートされているVSSに行くだろう。バックアップソフトウェアはハッキングに頼るべきではありません。

+0

fopenを使用する代わりに、私はCreateFileを使用しますか? –

+0

@Brunoどのような方法を使用しています。彼らはすべて最終的にCreateFileを呼び出します。 –

+0

バグは申し訳ありませんが、私はduplicateHandle()について読んでいましたが、プロセスハンドルを複製して、他のプロセスが使用しているのと同じファイルを使用できるようにすることはできますか? –

関連する問題