开发者

Duplicate file handle to avoid Sharing Violation Error Message

I´m trying to work on a incremental backup system and when I run my script, I always get the "Cannot open file due to sharing violation" as the file I´m trying to open to do the incremental backup is already in use by another process. I know I can kill this process, free up the file and do the incremental backup, but that´s something I really have to avoid. I´ve read that with the Win32 API I could duplicate the file handler, so how should I do.

Here is a piece of my code:

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 op开发者_开发知识库en 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

Could you please help me?


If the file's locked by another process, you can't open it. Period.

Windows has a Volume Shadow Copy service (VSS) specifically for the purpose of building backup clients. It has provisions for copying locked files, ensuring consistent snapshots etc. The API is a bit convoluted but there's no other way if you want a robust backup solution.

You can look at an open-source HoboCopy backup tool for an example of using VSS.

EDIT: After some googling I've found two examples that do what you want. I can't guarante these work, but at least they look plausible.

The first example, clear and well-commented (the language is some dialect of Basic, but very readable):

$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

Another messy example is at http://forum.sysinternals.com/topic7974.html.

Note that both examples use Native API functions and some of their undocumented functionality.

The basic steps are:

  1. Obtain file handle value as seen by the locking process. This is done by calling NtQuerySystemInformation to get the list of all handles in the system and NtQueryObject to find the one with matching file name. Note that file name must be converted to NT device format.
  2. Open the process owning the handle (OpenProcess) with suffucuent privileges (PROCESS_DUP_HANDLE and PROCESS_SUSPEND_RESUME) and call DuiplicateHandle (with that process as the source, handle value from step 1, and your process as the destination).
  3. Use NtSuspendProcess to "pause" the process and prevent it from modifying file pointer and file content).
  4. Copy the file using duplicated handle.
  5. Restore file pointer, close the handle, call NtResumeProcess to unpause the process.

EDIT2: Personally I have used another way to access locked files - raw disk access with manual NTFS parsing. E.g. given the file name find its MFT entry, decode data run locations and read them from the raw disk. Raw disk access is always available (provided you have admin privileges), therefore any file is readable. The downside is zero consistency guarantee so it isn't suitable for backup purposes.

PS. If I were you I'd still go with the officially supported VSS. Backup software should not rely on hacks.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜