kernel32: Implement ReplaceFileA/ReplaceFileW

Erich Hoover ehoover at mines.edu
Thu Mar 1 01:59:54 CST 2007


Skipped content of type multipart/alternative-------------- next part --------------
/**************************************************************************
 *           ReplaceFileW   (KERNEL32.@)
 *           ReplaceFile    (KERNEL32.@)
 */
BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName,
                         LPCWSTR lpBackupFileName, DWORD dwReplaceFlags,
                         LPVOID lpExclude, LPVOID lpReserved)
{
    BY_HANDLE_FILE_INFORMATION ifoReplaced, ifoReplacement;
    HANDLE hReplaced, hReplacement, hBackup;
    static const int buffer_size = 65536;
    BOOL skipBackup = FALSE, ret = FALSE;
    char *buffer;
    DWORD count;
    
    if (dwReplaceFlags)
        FIXME("Ignoring flags %x\n", dwReplaceFlags);
    /* First two arguments are mandatory */
    if (!lpReplacedFileName || !lpReplacementFileName)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    /* Create a copying buffer */
    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size )))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }
    /*
     * Open the replacement file for reading, writing, and deleting
     * (writing and deleting are needed when finished)
     */
    if ((hReplacement = CreateFileW(lpReplacementFileName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
    {
        goto replace_fail_1;
    }
    /* Obtain the file attributes from the replacement file */
    if (!GetFileInformationByHandle( hReplacement, &ifoReplacement ))
    {
        WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(lpReplacementFileName));
        goto replace_fail_2;
    }
    /* Open the "replaced" file for reading and writing */
    if ((hReplaced = CreateFileW(lpReplacedFileName, GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
        ifoReplacement.dwFileAttributes, hReplacement)) == INVALID_HANDLE_VALUE)
    {
        if ( GetLastError() == ERROR_FILE_NOT_FOUND )
        {
            /* If "replaced" does not exist then create it for the write, but skip backup */
            if ((hReplaced = CreateFileW(lpReplacedFileName, GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, ifoReplacement.dwFileAttributes,
                hReplacement)) == INVALID_HANDLE_VALUE)
            {
                goto replace_fail_2;
            }
            skipBackup = TRUE;
        }
        else
        {
            /* Inappropriate permissions to remove "replaced" */
            SetLastError( ERROR_UNABLE_TO_REMOVE_REPLACED );
            goto replace_fail_2;
        }
    }
    /* If the user wants a backup then that needs to be performed first */
    if ( lpBackupFileName && !skipBackup )
    {
        /* Obtain the file attributes from the "replaced" file */
        if (!GetFileInformationByHandle( hReplaced, &ifoReplaced ))
        {
            WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(lpReplacedFileName));
            goto replace_fail_3;
        }
        /* If an existing backup exists then copy over it */
        if ((hBackup = CreateFileW(lpBackupFileName, GENERIC_WRITE,
            FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, ifoReplaced.dwFileAttributes,
            hReplaced)) == INVALID_HANDLE_VALUE)
        {
            goto replace_fail_3;
        }
        /* Actually copy the "replaced" file into the backup file */
        while (ReadFile( hReplaced, buffer, buffer_size, &count, NULL ) && count)
        {
            char *p = buffer;
            while (count != 0)
            {
                DWORD res;
                if (!WriteFile( hBackup, p, count, &res, NULL ) || !res)
                {
                    /* on failure we need to cleanup all our resources */
                    CloseHandle( hBackup );
                    goto replace_fail_3;
                }
                p += res;
                count -= res;
            }
        }
        /* If the file was bigger before then end it after the last new write */
        SetEndOfFile( hBackup );
        /* Set the filetime of the backup to that of the "replaced" file */
        SetFileTime( hBackup, NULL, NULL, &ifoReplaced.ftLastWriteTime );
        CloseHandle( hBackup );
        /* Seek back to the beginning of the file */
        SetFilePointer( hReplaced, 0, NULL, FILE_BEGIN );
    }
    /*
     * Now that the backup has been performed (if requested), copy the replacement
     * into place
     */
    while (ReadFile( hReplacement, buffer, buffer_size, &count, NULL ) && count)
    {
        char *p = buffer;
        while (count != 0)
        {
            DWORD res;
            if (!WriteFile( hReplaced, p, count, &res, NULL ) || !res)
            {
                /* on failure we need to cleanup all our resources */
                SetLastError( ERROR_UNABLE_TO_MOVE_REPLACEMENT);
                goto replace_fail_3;
            }
            p += res;
            count -= res;
        }
    }
    /* If the file was bigger before then end it after the last new write */
    SetEndOfFile( hReplaced );
    /* Set the filetime of the "replaced" file to that of the replacement */
    SetFileTime( hReplaced, NULL, NULL, &ifoReplacement.ftLastWriteTime );
    /* 
     * Delete the replacement file, note that this delete won't really occur
     * until the original handle is released.
     */
    if (!DeleteFileW( lpReplacementFileName ))
    {
        /*
         * This case should never occur, we've already checked permissions earlier
         * and we are holding the file handle open.
         */
        ERR("Replacement file may not be deleted!\n");
    }
    ret = TRUE;

    /* Clean up all allocated resources */
replace_fail_3:
    CloseHandle( hReplaced );
replace_fail_2:
    CloseHandle( hReplacement );
replace_fail_1:
    HeapFree( GetProcessHeap(), 0, buffer );
    return ret;
}


More information about the wine-devel mailing list