I see your point. However, since the function you are implementing is in
kernel32 anyway you could abstract it away and make both functions
(CopyFile and ReplaceFile) call some internal function. That way you
would get rid of the locking completly which is argueably somewhat ugly.
On closer inspection, CreateFile actually seems to take care of this with
the "security attributes" and "template" parameters.  It does not look like
the security attributes are implemented yet (or the call to get the security
attributes for a file) but since CreateFile already handles this it seems
unnecessary to add a new break-out function.  You will likely find the
attached more to your liking.

Erich Hoover
[EMAIL PROTECTED]

On 2/28/07, Felix Nawothnig <[EMAIL PROTECTED]> wrote:

Erich Hoover wrote:
>>>> The "right" way would probably to do the copying yourself by
>>>> read/write.. but I dunno.
>>> Except that it would ignore the permissions issues that have already
>>> been coded into the copy routines (and any updates that may eventually
>> No, CreateFile (and friends) does the permissions checks (which you
>> would still have to call).
> That was worded poorly, Copy/Move already handle copying file attributes

> and I imagine would eventually implement copying the access control list
> information.  Implementing ReplaceFile as calls to either Copy or Move
> takes these issues into account.

I see your point. However, since the function you are implementing is in
kernel32 anyway you could abstract it away and make both functions
(CopyFile and ReplaceFile) call some internal function. That way you
would get rid of the locking completly which is argueably somewhat ugly.

Felix


/**************************************************************************
 *           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;
}


Reply via email to